Skip to content

Deployment

EndoCore is a standard ASGI app served by uvicorn. Anything that runs ASGI runs EndoCore.

The ASGI entry point

# provided by the package
endocore.asgi:create_app        # a factory that builds an Application from the CWD

create_app() reads the current working directory as the app root and honours three env vars:

  • ENDOCORE_DEV=1 — turn dev mode on (the in-process file watcher, /docs + /openapi.json, and a relaxed websocket same-origin check for a local frontend on another port). endo dev sets this for you; a bare uvicorn endocore.asgi:create_app --factory with no env vars at all is off by default — dev mode is opt-in, not opt-out, so forgetting to set anything fails toward the safer production posture.
  • ENDOCORE_DEFAULT_VERSION=latest — resolve version-less paths.
  • ENDOCORE_OPENAPI=1 — serve /docs + /openapi.json even with dev mode off. Off by default in production on purpose — opt in explicitly if you want the schema/UI publicly reachable.

Uvicorn (single process)

uvicorn endocore.asgi:create_app --factory --host 0.0.0.0 --port 8000

Uvicorn workers via Gunicorn (multi-process)

pip install gunicorn
gunicorn "endocore.asgi:create_app()" \
    --worker-class uvicorn.workers.UvicornWorker \
    --workers 4 --bind 0.0.0.0:8000

Rule of thumb: workers = 2 × CPU cores + 1. Because the ORM offloads to a threadpool, workers keep the event loop free under DB load.

Docker

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV ENDOCORE_DEV=0
EXPOSE 8000
CMD ["uvicorn", "endocore.asgi:create_app", "--factory", "--host", "0.0.0.0", "--port", "8000"]

requirements.txt:

endocore[postgres,files,redis,pydantic]
gunicorn

For production, prefer Gunicorn + Uvicorn workers:

CMD ["gunicorn", "endocore.asgi:create_app()", \
     "--worker-class", "uvicorn.workers.UvicornWorker", \
     "--workers", "4", "--bind", "0.0.0.0:8000"]

Nginx reverse proxy

server {
    listen 80;
    server_name api.example.com;
    location / {
        proxy_pass         http://127.0.0.1:8000;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   X-Request-ID      $request_id;
    }
    location /ws/ {                      # websockets
        proxy_pass         http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade    $http_upgrade;
        proxy_set_header   Connection "upgrade";
    }
}

Then trust the proxy headers in your app:

from endocore.middleware import proxy_headers_middleware
middlewares = [proxy_headers_middleware(trusted=["127.0.0.1"])]

systemd service

# /etc/systemd/system/endocore.service
[Unit]
Description=EndoCore app
After=network.target

[Service]
WorkingDirectory=/srv/app
Environment=ENDOCORE_DEV=0
ExecStart=/srv/app/.venv/bin/gunicorn endocore.asgi:create_app() \
  --worker-class uvicorn.workers.UvicornWorker --workers 4 --bind 0.0.0.0:8000
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now endocore

Migrations on deploy

Run migrations before switching traffic:

endo migrate            # apply pending migrations
endo showmigrations     # verify

PaaS (Render / Railway / Fly.io / Heroku-style)

  • Build: pip install -r requirements.txt
  • Start: gunicorn endocore.asgi:create_app() --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:$PORT
  • Env: your DATABASE_URL, secrets for cookies/CSRF and ENDOCORE_FILE_KEY (dev mode is off by default — no ENDOCORE_DEV needed unless you want it on).

Production checklist

  • [ ] Don't set ENDOCORE_DEV=1 (it's off by default; no watcher, /docs off).
  • [ ] Leave ENDOCORE_OPENAPI unset unless you deliberately want the schema public — don't set it to "1" out of habit.
  • [ ] Multiple workers behind a proxy.
  • [ ] endo migrate on each release.
  • [ ] Secrets from env (cookie/CSRF secret, ENDOCORE_FILE_KEY, DB creds).
  • [ ] Security middleware enabled (see Security).
  • [ ] TLS terminated at the proxy; proxy_headers_middleware configured.