Skip to content
infra Changes To NATS Server — Docker Image
Login

Changes To NATS Server — Docker Image

Initial version of "NATS Server — Docker Image"






































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# 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