Skip to content
infra Artifact [7f0120ea74]
Login

Artifact [7f0120ea74]

Artifact 7f0120ea7462b9b58f6757bddd2492c7d5bb0fa29dce94ddf814dfa169784063:

Wiki page [NATS Server — Docker Image] by dmestas on 2026-03-15 20:40:12.
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` → `<server-ip>`
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