Skip to content
infra NATS Server — Docker Image
Login

NATS Server — Docker Image

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 for the full design.


How It Works

docker build fossil-nats:2.10.22 OPERATOR_JWT SYS_ACCOUNT_KEY /var/nats/tls/ (ro) /var/nats/jwt/ (rw) /var/nats/jetstream/ (rw) env vars volumes docker-entrypoint.sh nats-server -c /tmp/nats-server.conf
    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

cd ~/Projects/infra/nats
sudo docker build -t fossil-nats:2.10.22 .

Run

Get the required values

# 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

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:

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

# 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):

sudo ufw allow 4222/tcp
sudo ufw status | grep 4222

On another host (varies by provider):

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

# 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:

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. The Docker image is provided for: