# Deployment: FreeBSD Jail with PostgreSQL 18 Layout: host nginx terminates TLS and proxies `/chess/api/` and `/chess/health` to `chess-server` in a Bastille jail. The rc.d service is `chessd`, running as the unprivileged `chess` account (no shell, no password). PostgreSQL 18 runs in the same jail and listens **only on its Unix socket**. The `chess` role reaches only the `chess` database through peer authentication, so no database password exists. ``` browser ──TLS──▶ host nginx ──HTTP──▶ jail: chessd (chess-server, API port) │ Unix socket, peer auth ▼ jail: PostgreSQL 18 (no TCP listener) ``` `host#` runs on the Bastille host as root; `jail#` runs inside the jail as root (`bastille console `). Addresses in this document are placeholders. For a native Linux install with systemd, see [deployment-linux.md](deployment-linux.md); database recipes are in [database.md](database.md). ## Scripted Installation and Upgrade The scripts in [`deploy/`](../deploy) are idempotent; rerun them to upgrade. Their header comments list every input. 1. **Host, once:** give the jail its own System V shared-memory namespace, which PostgreSQL needs, and restart it: ```sh host# sh deploy/freebsd/host.sh ``` 2. **Jail, once:** install the packages: ```sh jail# pkg install postgresql18-server postgresql18-client stockfish ``` 3. **Build** the release (FreeBSD `make` or GNU make) and copy the server binary and a checkout of this repository into the jail: ```sh make server-freebsd # bin/chess-server-freebsd-amd64 ``` 4. **Jail:** install or upgrade the service. `TRUSTED_PROXIES` is the address the host nginx connects from, as seen inside the jail. ```sh jail# CHESS_BINARY=/tmp/chess-server-freebsd-amd64 \ TRUSTED_PROXIES= \ ENABLE_LOG_ROTATION=yes ENABLE_BACKUP=yes \ sh deploy/freebsd/setup-jail.sh ``` The script stops `chessd`, installs everything, starts it again, and fails unless `/health` reports `"storage":"ok"`. 5. **Host:** point nginx at the jail ([`deploy/nginx-chess.conf`](../deploy/nginx-chess.conf)) and publish the web client (below). `setup-jail.sh` inputs: | Variable | Default | Meaning | |---|---|---| | `CHESS_BINARY` | required | `chess-server` binary to install | | `TRUSTED_PROXIES` | unset (warns) | Stored as `chessd_trusted_proxies` in `rc.conf` | | `SPLIT_PRIVILEGES` | `no` | `yes` for a DML-only runtime role (below) | | `ENABLE_LOG_ROTATION` | `no` | newsyslog entry for the service log | | `ENABLE_BACKUP` | `no` | Nightly `pg_dump` via cron | The service layout (account, home, binary, log, listen address, DSN, key) comes from `rc.conf` `chessd_*` settings, with the defaults of [`rc.d/chessd`](../deploy/freebsd/rc.d/chessd): account `chess`, home `/home/chess`, binary `~/bin/chess-server`, log `/var/log/chessd.log`, `0.0.0.0:8080`, DSN `postgres:///chess?host=/tmp`, key `~/jwt.key`. What the script changes, in order: | Step | Result | |---|---| | Preflight | Requires root, PostgreSQL 17+ installed, `chessd_user=chess`; refuses an `rc.conf` `chessd_flags` that still contains `-storage-path`; warns if `stockfish` is missing | | Cluster | `initdb` only when none exists: UTF-8, builtin `C.UTF-8` locale, peer local auth, host auth rejected | | `pg_hba.conf` | Only `postgres` (all databases) and `chess` (database `chess`), both `local` + `peer`; original kept as `pg_hba.conf.orig`. This replaces the trust-everything default of FreeBSD's standard `initdb` flags | | Listener | `ALTER SYSTEM SET listen_addresses = ''` (no TCP) and a restart, only when TCP is still enabled | | Provisioning | [`setup.sql`](../deploy/postgresql/setup.sql) when neither role nor database `chess` exists | | Service stop | `service chessd onestop` through the installed rc.d script | | Binary | `root:wheel 0555`; the previous file kept as `chess-server.prev` | | rc.d | [`rc.d/chessd`](../deploy/freebsd/rc.d/chessd) installed; a differing previous copy saved as `/var/backups/chessd.rc.