D 2026-03-15T20:40:12.782 L NATS\sServer\s—\sDocker\sImage N text/x-markdown U dmestas W 6634 # NATS Server — Docker Image A minimal Docker image for running the fossil-nats server in a container. Image size: **24 MB** (based on `nats:2.10.22-alpine`, no extra packages added). See [NATS Server — Setup & Architecture](NATS Server — Setup & Architecture) for the full design. --- ## How It Works ``` pikchr right BLD: box "docker build" fill 0xfff0cc width 1.4 height 0.4 rad 4px move 0.15 arrow color 0x888888 move 0.15 IMG: box "fossil-nats:2.10.22" color white fill 0x2a4a8a width 1.8 height 0.4 rad 4px down ENV: box "OPERATOR_JWT" fill 0xd0f0d0 width 1.4 height 0.35 rad 4px at IMG.s + (-1.0, -0.6) SYS: box "SYS_ACCOUNT_KEY" fill 0xd0f0d0 width 1.4 height 0.35 rad 4px at IMG.s + (0.6, -0.6) TLS: box "/var/nats/tls/ (ro)" fill 0xd0eeff width 1.4 height 0.35 rad 4px at IMG.s + (-1.0, -1.1) JWT: box "/var/nats/jwt/ (rw)" fill 0xd0eeff width 1.4 height 0.35 rad 4px at IMG.s + (0.6, -1.1) JST: box "/var/nats/jetstream/ (rw)" fill 0xd0eeff width 1.7 height 0.35 rad 4px at IMG.s + (-0.15, -1.6) arrow from ENV.n to IMG.s + (-1.0, -0.05) color 0x888888 <- arrow from SYS.n to IMG.s + (0.6, -0.05) color 0x888888 <- arrow from TLS.n to IMG.s + (-1.0, -0.05) color 0x888888 <- arrow from JWT.n to IMG.s + (0.6, -0.05) color 0x888888 <- arrow from JST.n to IMG.s + (-0.15, -0.05) color 0x888888 <- text "env vars" small at IMG.s + (-0.2, -0.35) text "volumes" small at IMG.s + (-0.2, -0.85) right EP: box "docker-entrypoint.sh" fill 0xf0f0f0 width 1.8 height 0.4 rad 4px at IMG.e + (0.8, -0.3) arrow from IMG.e right 0.15 then to EP.w color 0x888888 move 0.15 arrow color 0x888888 NS: box "nats-server -c /tmp/nats-server.conf" fill 0xd0f0d0 width 2.8 height 0.4 rad 4px ``` The entrypoint: 1. Sources `/etc/nats/operator.env` if present (mounted volume) 2. Validates `OPERATOR_JWT` and `SYS_ACCOUNT_KEY` are set 3. Renders `nats-server.conf.template` → `/tmp/nats-server.conf` using `sed` 4. `exec nats-server -c /tmp/nats-server.conf` No extra packages are installed — config substitution uses `sed` from busybox (already in `nats:alpine`). --- ## Files | File | Purpose | |------|---------| | `nats/Dockerfile` | Image definition | | `nats/docker-entrypoint.sh` | Startup script — renders config, starts server | | `nats/nats-server.conf.template` | Config template with `$OPERATOR_JWT` / `$SYS_ACCOUNT_KEY` | | `nats/.dockerignore` | Excludes `creds/` from build context | --- ## Build ```sh cd ~/Projects/infra/nats sudo docker build -t fossil-nats:2.10.22 . ``` --- ## Run ### Get the required values ```sh # Operator JWT (already in /etc/nats/operator.env on the server) OPERATOR_JWT=$(sudo sed 's/OPERATOR_JWT=//' /etc/nats/operator.env) # SYS account public key SYS_ACCOUNT_KEY=$(nsc describe account --name SYS 2>&1 \ | grep 'Account ID' \ | sed 's/.*| Account ID *| *\([^ ]*\) .*/\1/') ``` ### Start the container ```sh sudo docker run -d \ --name fossil-nats \ -p 4222:4222 \ -e OPERATOR_JWT="$OPERATOR_JWT" \ -e SYS_ACCOUNT_KEY="$SYS_ACCOUNT_KEY" \ -v /var/nats/tls:/var/nats/tls:ro \ -v /var/nats/jwt:/var/nats/jwt \ -v /var/nats/jetstream:/var/nats/jetstream \ fossil-nats:2.10.22 ``` Or mount `operator.env` as a file instead of env vars: ```sh sudo docker run -d \ --name fossil-nats \ -p 4222:4222 \ -e SYS_ACCOUNT_KEY="$SYS_ACCOUNT_KEY" \ -v /etc/nats/operator.env:/etc/nats/operator.env:ro \ -v /var/nats/tls:/var/nats/tls:ro \ -v /var/nats/jwt:/var/nats/jwt \ -v /var/nats/jetstream:/var/nats/jetstream \ fossil-nats:2.10.22 ``` --- ## Verify ```sh # Check logs sudo docker logs fossil-nats # Connect and list streams nats stream ls \ --server tls://sync.craftdesign.group:4222 \ --creds ~/Projects/infra/nats/creds/edgesync-admin.creds # Publish a test message nats pub fossil.edgesync.test "hello" \ --server tls://sync.craftdesign.group:4222 \ --creds ~/Projects/infra/nats/creds/edgesync-admin.creds ``` --- ## Network Requirements Before running the container, the operator must complete these steps: ### 1. Open port 4222 in the firewall **On vps-nbg1 (UFW):** ```sh sudo ufw allow 4222/tcp sudo ufw status | grep 4222 ``` **On another host (varies by provider):** - Open TCP port 4222 inbound in your cloud provider's security group / firewall rule. - There is no other NATS port to open for single-node TLS-only setup. ### 2. Add a DNS A record Clients connect using the hostname in the TLS cert. The cert is currently issued for `sync.craftdesign.group`. For a **new host**, either: | Option | When to use | Steps | |--------|-------------|-------| | Reuse the existing cert | Deploying on vps-nbg1 or a machine that can serve `sync.craftdesign.group` | Mount `/var/nats/tls` — no DNS change needed on the host | | Issue a new cert for your domain | Deploying on a new server with its own hostname | Run `install.sh` (Phase 1) on that server — it issues a cert for the `DOMAIN` you set | | Self-signed cert (dev/test only) | Local dev machine, no public DNS | Generate a self-signed cert and mount it; clients must use `--tlsca` | For the standard case (new server, new domain): 1. Create DNS A record: `your-nats.example.com` → `` 2. Set `DOMAIN=your-nats.example.com` in `install.sh` before running 3. The TLS cert is issued automatically via acme.sh DNS-01 ### 3. Verify DNS before starting ```sh # From any machine — must resolve before TLS cert is issued nslookup your-nats.example.com # or dig +short your-nats.example.com ``` --- ## TLS Note The cert baked into the server config is for `sync.craftdesign.group` (Let's Encrypt, auto-renewed via acme.sh on the host). When running the container: - **On vps-nbg1**: mount `/var/nats/tls` — the cert is already there, and the DNS resolves correctly. - **Elsewhere**: replace `/var/nats/tls` with a cert valid for your hostname, and update the `cert_file` / `key_file` paths in `nats-server.conf.template` if they differ. Clients must connect using the hostname the cert was issued for (`sync.craftdesign.group`), not by IP, for TLS verification to pass. --- ## On This Server (vps-nbg1) NATS runs under **systemd** (not Docker) in production — see [NATS Server — Operations Runbook](NATS Server — Operations Runbook). The Docker image is provided for: - Deploying to a second node in a future cluster - Running NATS on a dev machine or CI runner that connects as a leaf node - Testing config changes in isolation before applying to the production systemd instance Z eb5917bf1db6d49d58dc14ee848d0745