deploy: production deployment for noema.dixon.cx
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.
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
# 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
|
||||
```
|
||||
Reference in New Issue
Block a user