The systemd unit, nginx vhost, build-and-restart script and operator notes for running Noema as a Docker container behind nginx against the host PostgreSQL cluster. Secrets stay in /etc/noema/noema.env, outside the repo.
75 lines
3.2 KiB
Markdown
75 lines
3.2 KiB
Markdown
# Production deployment (noema.dixon.cx)
|
|
|
|
Noema runs as a Docker container supervised by systemd, talks to the host
|
|
PostgreSQL 16 cluster over the docker bridge, and sits behind nginx. Access
|
|
control is Noema's own sign-in page. Cloudflare proxies the hostname
|
|
(wildcard `*.dixon.cx`).
|
|
|
|
```
|
|
Cloudflare -> nginx :443 (TLS) -> 127.0.0.1:29630 -> container :8080 -> postgres 172.17.0.1:5432
|
|
```
|
|
|
|
## Deploying a new version
|
|
|
|
```sh
|
|
git pull
|
|
deploy/prod/build.sh
|
|
```
|
|
|
|
`build.sh` builds `noema:<git describe>` and `noema:latest`, then restarts the
|
|
`noema` service. Migrations are applied automatically when the daemon starts.
|
|
Roll back with `docker tag noema:<older-version> noema:latest && sudo systemctl restart noema`
|
|
(`docker images noema` lists the versions you have).
|
|
|
|
## Where things live
|
|
|
|
| What | Where |
|
|
|---|---|
|
|
| systemd unit | `/etc/systemd/system/noema.service` (source: `deploy/prod/noema.service`) |
|
|
| Environment and secrets | `/etc/noema/noema.env` (root, 0600): DB URL, master key, bootstrap admin |
|
|
| nginx vhost | `/etc/nginx/sites-enabled/noema.dixon.cx.conf` (source: `deploy/prod/noema.dixon.cx.conf`) |
|
|
| TLS certificate | `/etc/letsencrypt/live/noema.dixon.cx` (certbot, nginx authenticator, auto-renews) |
|
|
| Research folder | `/srv/noema/research` mounted at `/research` (owned by uid 10001) |
|
|
| Database | `noema` database, `noema` role on the host cluster; `vector` extension created by `postgres` |
|
|
| Logs | `journalctl -u noema -f` (JSON); nginx `/var/log/nginx/noema_*.log` |
|
|
|
|
The daemon ignores proxy headers, so it sees every visitor as the docker
|
|
gateway address: the login rate limit (20/min) is shared by everyone, and the
|
|
per-account lockout is what actually stops password guessing. Use a strong
|
|
admin password and enable TOTP on the account page.
|
|
|
|
**Back up `NOEMA_MASTER_KEY`.** It encrypts provider API keys stored in the
|
|
database; losing it means re-entering every key.
|
|
|
|
## Common tasks
|
|
|
|
```sh
|
|
# noemactl against the running instance
|
|
docker exec -it noema noemactl --help
|
|
|
|
# psql as the app (from the host use the postgres superuser instead)
|
|
sudo -u postgres psql noema
|
|
```
|
|
|
|
## Rebuilding from scratch
|
|
|
|
Applies to a fresh host; everything below was done once on this one.
|
|
|
|
```sh
|
|
sudo -u postgres psql -c "CREATE ROLE noema LOGIN PASSWORD '...'" -c "CREATE DATABASE noema OWNER noema"
|
|
sudo -u postgres psql -d noema -c "CREATE EXTENSION vector" # needs superuser; migration 0005 expects it
|
|
# pg_hba.conf must allow 172.17.0.0/16 and postgresql.conf must listen on 172.17.0.1
|
|
|
|
sudo install -d -m 700 /etc/noema && sudo install -d -o 10001 -g 10001 /srv/noema/research
|
|
sudoedit /etc/noema/noema.env # keys as in .env.example, plus:
|
|
# NOEMA_ENV=production NOEMA_HTTP_ADDR=0.0.0.0:8080 NOEMA_RESEARCH_DIR=/research
|
|
# NOEMA_DATABASE_URL=postgres://noema:PASS@172.17.0.1:5432/noema?sslmode=disable
|
|
# NOEMA_MASTER_KEY=$(openssl rand -base64 32) NOEMA_BOOTSTRAP_ADMIN_PASSWORD=...
|
|
|
|
sudo cp deploy/prod/noema.service /etc/systemd/system/ && sudo systemctl daemon-reload
|
|
deploy/prod/build.sh && sudo systemctl enable noema
|
|
|
|
sudo certbot certonly --nginx -d noema.dixon.cx --key-type ecdsa
|
|
sudo cp deploy/prod/noema.dixon.cx.conf /etc/nginx/sites-enabled/ && sudo nginx -t && sudo systemctl reload nginx
|
|
```
|