coder: initial import of deployed stack (v2.35.3 compose, docker template, SSOT vault)

This commit is contained in:
Antigravity
2026-08-20 10:50:27 +02:00
commit 1fc121ed7e
19 changed files with 2176 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
---
title: "🧱 Architecture"
description: "How the Coder control plane, its private postgres, its provisioner and its two traffic planes fit together on the loca operator host."
source: "verified deployment on the loca operator host, 2026-08-19"
reviewed: 2026-08-19
---
# 🧱 Architecture
The deployed shape in one paragraph: nginx terminates TLS for `code.loca.zone` and proxies everything to a loopback-published Coder container, which stores all state in a private postgres container and builds workspaces by talking to the host docker socket. No component of this stack is reachable from the network except through the two nginx vhosts.
## 🧭 Two traffic planes, one vhost
- human plane: browser to dashboard, web terminal and path-based workspace apps, authenticated by the host-scoped `coder_session_token` cookie
- machine plane: the `coder` CLI, workspace agents and DERP relay traffic, authenticated by the `Coder-Session-Token` header over long-lived websockets
- both planes enter through the same `code.loca.zone:443` server block, which is exactly why an `auth_request` gate cannot be applied to only one of them; see [[operations/auth-model]]
## 🗺️ Pages
| Need | Page |
| --- | --- |
| Diagram, exact container, volume and port wiring | [[architecture/runtime-topology]] |
| Path, port and env var lookup table | [[reference/config-paths]] |
| What is deliberately not enabled | [[reference/limits]] |
| Operational commands | [[operations/index]] |
## 📚 Sources
- https://coder.com/docs/admin/networking
- https://coder.com/docs/reference/api/authentication

View File

@@ -0,0 +1,107 @@
---
title: "🗺️ Runtime topology"
description: "Exact wiring of the Coder stack: nginx 443 to loopback 52226 to container coder, private postgres over the compose network, the docker.sock provisioner path, and the separate agent and CLI path."
source: "verified deployment on the loca operator host, 2026-08-19"
reviewed: 2026-08-19
---
# 🗺️ Runtime topology
## 🧩 Diagram
```mermaid
flowchart TD
browser["Browser: dashboard, web terminal, path-based apps"]
cli["coder CLI and workspace agents"]
nginx["nginx 443 vhost code.loca.zone, TLS lineage code.loca.zone, no auth_request"]
wikivhost["nginx 443 vhost wiki.code.loca.zone, static root wikis/code/current"]
publish["host publish 127.0.0.1:52226 to container 7080"]
coder["container coder, ghcr.io/coder/coder:v2.35.3, listens 0.0.0.0:7080, mem_limit 8g"]
db["container coder-database, postgres:17, volume coder_pgdata, mem_limit 2g"]
sock["host /var/run/docker.sock, gid 105, bind-mounted into coder"]
ws["workspace containers built by the built-in provisioner"]
browser -->|https 443| nginx
cli -->|https 443 with Coder-Session-Token| nginx
browser -->|https 443| wikivhost
nginx -->|proxy_pass, http 1.1, Upgrade, read timeout 86400| publish
publish --> coder
coder -->|compose network, database port 5432, sslmode disable| db
coder -->|terraform docker provider| sock
sock --> ws
ws -->|agent websockets back to CODER_ACCESS_URL| nginx
```
## 🌐 Browser and CLI path into the server
- Browser or `coder` CLI to `https://code.loca.zone`
- nginx server block, from `/home/loca/dev/coder/deploy/code.nginx.conf`
- `listen 443 ssl`, `server_name code.loca.zone`
- `include snippets/security-headers-app.conf`
- `client_max_body_size 128m` so template tarball pushes are not truncated
- no `auth_request`, no Authelia include: deliberate, see [[operations/auth-model]]
- `location /`
- `proxy_pass http://127.0.0.1:52226`
- `proxy_http_version 1.1`
- `proxy_set_header Upgrade $http_upgrade`
- `proxy_set_header Connection $connection_upgrade`
- `proxy_set_header Host $host`, `X-Real-IP $remote_addr`, `X-Forwarded-For $proxy_add_x_forwarded_for`, `X-Forwarded-Proto $scheme`
- `proxy_buffering off` so the web terminal and log streams are not held in nginx buffers
- `proxy_read_timeout 86400` because the dashboard, web terminal and agent websockets are long-lived; upstream requires that any reverse proxy between the control plane and clients or agents supports websockets
- TLS material `/etc/letsencrypt/live/code.loca.zone/fullchain.pem` and `privkey.pem`
- one lineage named `code.loca.zone` covering `code.loca.zone` and `wiki.code.loca.zone`, expiring 2026-11-17
- port 80 blocks exist for both hostnames: `/.well-known/acme-challenge/` from `/var/www/html`, everything else `301` to https
- Wiki vhost `wiki.code.loca.zone`
- `include snippets/security-headers.conf`, static `root /home/loca/dev/wikis/code/current`, `index index.html`
- immutable one-year cache for hashed assets, `try_files $uri $uri.html $uri/ =404` for pages
- shares the same certificate lineage; it is public by omission because Authelia `default_policy` is `bypass`
## 📦 Containers, volumes and the compose network
- compose project root `/home/loca/dev/coder`, so the compose project name is `coder`
- service `coder`, container name `coder`
- image `ghcr.io/coder/coder:v2.35.3`, `restart: unless-stopped`, `mem_limit: 8g`
- `depends_on` service `database` with `condition: service_healthy`, so the server never starts against an unready database
- volumes: `/var/run/docker.sock:/var/run/docker.sock` and named volume `coder_home` at `/home/coder`
- `group_add: "105"`, the host gid of the docker socket, read at write time with `getent group docker | cut -d: -f3`
- service `database`, container name `coder-database`
- image `postgres:17`, `restart: unless-stopped`, `mem_limit: 2g`
- named volume `coder_pgdata` at `/var/lib/postgresql/data`
- healthcheck `pg_isready -U coder -d coder` every 5s, timeout 5s, 12 retries
- publishes no host port at all; the only route to it is the compose network hostname `database` on port 5432
- named volumes as declared in `docker-compose.yml`: `coder_pgdata`, `coder_home`
- compose creates named volumes prefixed with the project name, so on the docker side they appear as `coder_coder_pgdata` and `coder_coder_home`
- always address them through `sudo -n docker compose` from `/home/loca/dev/coder` rather than by raw docker volume name
- the compose network is the only path between the two containers; nothing else on the host can reach postgres
## 🔌 Why 0.0.0.0:7080 inside and loopback outside
- `CODER_HTTP_ADDRESS=0.0.0.0:7080` binds all interfaces of the container's own network namespace
- a container-local `127.0.0.1` bind would be unreachable from the host publish path, so the server would appear dead even though it is healthy
- the publish spec `"127.0.0.1:52226:7080"` is the actual exposure boundary
- docker opens the port on the host loopback only, so the sole public route is nginx
- `52226` sits inside the loca.zone `51000-53000` application range and does not collide with `52222` pwiz, `52223` reserved for pwiz, or `52224` cdsr
- consequence: `CODER_TLS_ENABLE` is never set; nginx terminates TLS, and `CODER_ACCESS_URL=https://code.loca.zone` tells Coder the external scheme and hostname to advertise to browsers and agents
## 🏗️ Provisioner path to workspace containers
- the built-in provisioner runs inside the `coder` container; there is no external provisioner daemon
- template Terraform uses the `kreuzwerker/docker` provider, which talks to the bind-mounted `/var/run/docker.sock`
- `group_add: "105"` gives the non-root container user access to that socket; upstream's docker install guide sets this from `getent group docker | cut -d: -f3`
- workspace containers are therefore siblings on the host docker daemon, not children of the `coder` container
- the socket is never passed into a workspace; the risk model and its containment list live in [[operations/workspaces]]
## 🤖 Agent and CLI plane
- a workspace agent dials `CODER_ACCESS_URL`, that is `https://code.loca.zone`, and holds open `/api/v2` websockets for the duration of the workspace
- the `coder` CLI authenticates with the `Coder-Session-Token` header, never with a cookie
- upstream requires that all clients and agents can reach the server over HTTP or HTTPS and that any proxy in between supports websockets; `proxy_read_timeout 86400` plus `proxy_buffering off` satisfy that here
- relayed connections use the server's built-in DERP service over the same 443 vhost, so anything that interrupts unauthenticated-by-cookie requests interrupts workspace connectivity too
## 📚 Sources
- https://coder.com/docs/install/docker
- https://coder.com/docs/admin/setup
- https://coder.com/docs/admin/networking
- https://coder.com/docs/reference/api/authentication
- https://docs.docker.com/reference/compose-file/volumes/