# Production overview How DevPlace runs in production: a single host, two containers, and one set of files shared with the development server. See also [Deploy and update](/docs/production-deploy.html), [nginx and networking](/docs/production-nginx.html), and [Multi-worker and concurrency](/docs/production-concurrency.html). ## Topology Production is Docker Compose on a single host: - **app** - Uvicorn running `devplacepy.main:app` with 2 workers (`--proxy-headers`, `--forwarded-allow-ips '*'`). Not published directly; only reachable on the internal `appnet` network as `app:10500`. - **nginx** - the public front door. It terminates HTTP on the host `PORT` (default 10500), serves static assets and uploads from disk, forces downloads on user uploads, proxies the application and the Devii WebSocket, and optionally micro-caches GETs. There is no separate database service: the data lives in SQLite files on the host. ## Shared database and files This is the defining property of the setup: **production uses the same database and shares as the development server.** The app container bind-mounts the host project directory (`./` to `/app`) and runs as the host user (`DEVPLACE_UID:DEVPLACE_GID`, default `1000:1000`). `config.py` resolves the database to an absolute path under the project root, so with no `DEVPLACE_DATABASE_URL` override the container reads and writes the **same `devplace.db`** as `make dev`. The same bind mount also shares: - `devplace.db` (plus its `-wal` / `-shm` companions) - `static/uploads/` (user attachments and images) - `devii_lessons.db`, `devii_tasks.db` (Devii memory and tasks) - `notification-*.pem` (VAPID push keys) - `devplace-services.lock` (background-service coordination) Because the container runs as the host user, every file it writes stays owned by the developer account, with no permission conflicts. ## Why this is safe - **Concurrency.** SQLite runs in WAL mode with a 30s busy timeout, which allows concurrent readers and a serialized writer. The dev process and the container workers can use the file at the same time. - **One services owner.** The background services (news, bots, Devii hub) are guarded by an exclusive `fcntl.flock` on `devplace-services.lock`. Across every process that shares that file - dev and prod alike - exactly one acquires the lock and runs the services; the others skip them. News is never fetched twice and bots never double-run. - **One DB, one truth.** No `DEVPLACE_DATABASE_URL` override means nothing can silently diverge to a second database. ## Same-host requirement SQLite is a local-file database; it cannot be shared across machines. For production and development to share one `devplace.db`, they must run on the **same host and filesystem**. A second server would need its own database and a different architecture (this deployment intentionally does not do that). ## Code updates without rebuild Because the host project directory is bind-mounted over `/app`, the container runs the host's current source. A code update is `git pull` followed by a restart - no image rebuild. The image is only rebuilt when Python dependencies in `pyproject.toml` change (those are installed into the image at build time). See [Deploy and update](/docs/production-deploy.html).