Installation
Detailed installation guide for production environments.
System Requirements
- OS: Linux (Ubuntu 24.04 LTS, Debian 13 recommended) or macOS
- Architecture: x86_64 (amd64) or ARM64
- Memory: 512MB minimum, 1GB recommended
- Disk: 10GB minimum for registry storage
- Runtime: Docker or Podman with a Docker-compatible API version 1.40 or later
Download Gordon
Quick Install (Recommended)
(
set -euo pipefail
installer="$(mktemp)"
trap 'rm -f "$installer"' EXIT
curl -fsSL --output "$installer" https://gordon.bnema.dev/install
if command -v less >/dev/null 2>&1; then
less "$installer"
else
cat "$installer"
fi
bash "$installer"
)
Download and inspect the installer before executing it. The installer verifies the release archive checksum before installation and automatically detects your OS (Linux/macOS) and architecture (amd64/arm64), downloads the appropriate binary from GitHub releases, and installs it to /usr/local/bin.
Manual Installation
Manual archive installation requires downloading the matching checksums.txt release asset and verifying the archive's SHA-256 checksum before extraction. The checksum-verifying installer above is recommended.
Linux (x86_64)
wget https://github.com/bnema/gordon/releases/latest/download/{gordon_linux_amd64.tar.gz,checksums.txt}
grep -E '^[[:xdigit:]]{64} ([* ]?)gordon_linux_amd64\.tar\.gz$' checksums.txt | sha256sum --check --strict
tar -xzf gordon_linux_amd64.tar.gz
chmod +x gordon
sudo mv gordon /usr/local/bin/
Linux (ARM64) - for Raspberry Pi 4, AWS Graviton, Oracle Ampere, etc.
wget https://github.com/bnema/gordon/releases/latest/download/{gordon_linux_arm64.tar.gz,checksums.txt}
grep -E '^[[:xdigit:]]{64} ([* ]?)gordon_linux_arm64\.tar\.gz$' checksums.txt | sha256sum --check --strict
tar -xzf gordon_linux_arm64.tar.gz
chmod +x gordon
sudo mv gordon /usr/local/bin/
macOS (Apple Silicon)
curl -LO https://github.com/bnema/gordon/releases/latest/download/{gordon_darwin_arm64.tar.gz,checksums.txt}
grep -E '^[[:xdigit:]]{64} ([* ]?)gordon_darwin_arm64\.tar\.gz$' checksums.txt | shasum -a 256 --check
tar -xzf gordon_darwin_arm64.tar.gz
chmod +x gordon
sudo mv gordon /usr/local/bin/
macOS (Intel)
curl -LO https://github.com/bnema/gordon/releases/latest/download/{gordon_darwin_amd64.tar.gz,checksums.txt}
grep -E '^[[:xdigit:]]{64} ([* ]?)gordon_darwin_amd64\.tar\.gz$' checksums.txt | shasum -a 256 --check
tar -xzf gordon_darwin_amd64.tar.gz
chmod +x gordon
sudo mv gordon /usr/local/bin/
Verify installation:
gordon version
From Source
# Requires Go 1.21+
git clone https://github.com/bnema/gordon.git
cd gordon
make build
sudo mv gordon /usr/local/bin/
Container Runtime Setup
Docker
# Install Docker from your distribution's signed package repository.
sudo apt update
sudo apt install docker.io
# Add user to docker group
sudo usermod -aG docker $USER
newgrp docker
# Verify
docker run hello-world
Podman (Rootless)
Podman rootless mode provides enhanced security by running containers without root privileges.
# Install Podman
sudo apt update
sudo apt install -y podman
# Enable user namespaces
echo 'user.max_user_namespaces=28633' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# Setup subuid/subgid
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
# Enable Podman socket
systemctl --user enable --now podman.socket
# Configure insecure localhost registry (required — Podman blocks HTTP registries by default)
mkdir -p ~/.config/containers/registries.conf.d
cat > ~/.config/containers/registries.conf.d/gordon.conf <<EOF
[[registry]]
location = "localhost:5000"
insecure = true
EOF
Firewall Configuration
Gordon needs ports accessible for the registry and the public edge entrypoint.
Using firewalld
# Install and enable firewalld
sudo apt install -y firewalld
sudo systemctl enable --now firewalld
# Allow HTTP/HTTPS
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
# For rootless services, bind entrypoints.edge.address to a high port (for example :9000)
# and redirect the external TCP edge ports your deployment uses.
sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=9000
sudo firewall-cmd --permanent --add-forward-port=port=443:proto=tcp:toport=9000
# Apply changes
sudo firewall-cmd --reload
# Verify
sudo firewall-cmd --list-all
Using ufw
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 5000/tcp # Registry port (if accessing directly)
sudo ufw enable
Configuration
Gordon creates a default configuration on first run:
# Run once to generate config, then stop
gordon serve
# Press Ctrl+C
# Edit configuration
nano ~/.config/gordon/gordon.toml
Minimum required configuration:
[server]
registry_port = 5000
gordon_domain = "gordon.yourdomain.com"
[entrypoints.edge]
address = ":443"
protocol = "smart_tcp"
[routes]
"app.yourdomain.com" = "myapp:latest"
See Configuration Reference for all options.
Systemd Service
User Service (Recommended for Rootless)
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/gordon.service <<EOF
[Unit]
Description=Gordon Container Platform
After=podman.socket
[Service]
Type=simple
Restart=always
RestartSec=5
# Required for Podman rootless — tells Gordon where to find the Podman socket
Environment=XDG_RUNTIME_DIR=/run/user/%U
Environment=DOCKER_HOST=unix:///run/user/%U/podman/podman.sock
ExecStart=/usr/local/bin/gordon serve
[Install]
WantedBy=default.target
EOF
> **Docker users:** Omit the two `Environment=` lines above — Docker's socket path is set automatically.
# Enable and start
systemctl --user daemon-reload
systemctl --user enable --now gordon
# Enable linger (keep service running after logout)
sudo loginctl enable-linger $USER
# Check status
systemctl --user status gordon
System Service (Root Mode)
sudo cat > /etc/systemd/system/gordon.service <<EOF
[Unit]
Description=Gordon Container Platform
After=docker.service
Requires=docker.service
[Service]
Type=simple
Restart=always
RestartSec=5
ExecStart=/usr/local/bin/gordon serve
User=root
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now gordon
DNS Configuration
Point your domains to your server:
| Type |
Name |
Content |
Proxy |
| A |
app |
SERVER_IP |
Yes (Cloudflare) |
| A |
registry |
SERVER_IP |
Yes (Cloudflare) |
Note: Enable Cloudflare proxy (orange cloud) for automatic HTTPS. Gordon receives HTTP traffic from Cloudflare.
Cloudflare SSL Mode
Gordon serves public application traffic through HTTP-capable entrypoints such as entrypoints.edge with protocol = "smart_tcp". Choose the Cloudflare mode that matches how you want Cloudflare to reach your origin.
| Mode |
How it works |
Use when |
| Flexible |
Cloudflare terminates TLS; connects to Gordon with cleartext HTTP |
You want edge HTTPS only |
| Full (Strict) |
Cloudflare connects to Gordon over HTTPS with a valid cert |
You want end-to-end HTTPS using Gordon's public ACME certs or a static cert (tls_cert_file / tls_key_file) |
Wrong mode causes: 521 (Cloudflare can't connect) or 525 (TLS handshake failed).
Important: For Cloudflare-proxied HTTP paths, set proxy_allowed_ips with Cloudflare edge IPs — see Proxy Origin Allowlist below.
Rootless note: Unprivileged users can't bind privileged ports. Bind entrypoints.edge.address to a high port (for example :9000) and forward/map external ports to it via firewall or container settings.
Proxy Origin Allowlist
When Gordon serves HTTP paths through a smart TCP edge, direct non-localhost HTTP requests can be restricted to certificate onboarding paths. Cloudflare and other reverse proxies must be listed in proxy_allowed_ips to reach your applications.
Add Cloudflare edge IPs to your gordon.toml:
[server]
proxy_allowed_ips = [
"173.245.48.0/20", "103.21.244.0/22", "103.22.200.0/22",
"103.31.4.0/22", "141.101.64.0/18", "108.162.192.0/18",
"190.93.240.0/20", "188.114.96.0/20", "197.234.240.0/22",
"198.41.128.0/17", "162.158.0.0/15", "104.16.0.0/13",
"104.24.0.0/14", "172.64.0.0/13", "131.0.72.0/22",
]
Without this setting, Cloudflare traffic receives 403 Forbidden: Only certificate onboarding is available over HTTP.
Note: This is separate from [api.rate_limit] trusted_proxies, which controls IP extraction from X-Forwarded-For. Both should list your proxy IPs. See Proxy Origin IP Allowlist for details.
Installing a Specific Version or Pre-Release
# Install an exact version
curl -fsSL https://gordon.bnema.dev/install | GORDON_VERSION=v2.30.1 bash
# Install the latest pre-release instead of the latest stable release
curl -fsSL https://gordon.bnema.dev/install | GORDON_PRERELEASE=1 bash
By default, the installer resolves latest to the newest stable release and verifies the downloaded checksum before installing.
Verify Installation
# Check Gordon is running
systemctl --user status gordon
# View logs
journalctl --user -u gordon -f
# Test registry (from local machine)
docker login registry.yourdomain.com
docker pull alpine
docker tag alpine registry.yourdomain.com/test:latest
docker push registry.yourdomain.com/test:latest
Data Directories
Gordon stores data in the following locations:
| Path |
Purpose |
~/.config/gordon/gordon.toml |
Configuration file |
~/.gordon/ |
Default data directory |
~/.gordon/registry/ |
Container images |
~/.gordon/env/ |
Environment files |
~/.gordon/logs/ |
Application logs |
~/.gordon/secrets/ |
Secrets (unsafe backend only) |
source
1# Installation
2
3Detailed installation guide for production environments.
4
5## System Requirements
6
7- **OS**: Linux (Ubuntu 24.04 LTS, Debian 13 recommended) or macOS
8- **Architecture**: x86_64 (amd64) or ARM64
9- **Memory**: 512MB minimum, 1GB recommended
10- **Disk**: 10GB minimum for registry storage
11- **Runtime**: Docker or Podman with a Docker-compatible API version 1.40 or later
12
13## Download Gordon
14
15### Quick Install (Recommended)
16
17```bash
18(
19 set -euo pipefail
20 installer="$(mktemp)"
21 trap 'rm -f "$installer"' EXIT
22 curl -fsSL --output "$installer" https://gordon.bnema.dev/install
23 if command -v less >/dev/null 2>&1; then
24 less "$installer"
25 else
26 cat "$installer"
27 fi
28 bash "$installer"
29)
30```
31
32Download and inspect the installer before executing it. The installer verifies the release archive checksum before installation and automatically detects your OS (Linux/macOS) and architecture (amd64/arm64), downloads the appropriate binary from GitHub releases, and installs it to `/usr/local/bin`.
33
34### Manual Installation
35
36Manual archive installation requires downloading the matching `checksums.txt` release asset and verifying the archive's SHA-256 checksum before extraction. The checksum-verifying installer above is recommended.
37
38**Linux (x86_64)**
39
40```bash
41wget https://github.com/bnema/gordon/releases/latest/download/{gordon_linux_amd64.tar.gz,checksums.txt}
42grep -E '^[[:xdigit:]]{64} ([* ]?)gordon_linux_amd64\.tar\.gz$' checksums.txt | sha256sum --check --strict
43tar -xzf gordon_linux_amd64.tar.gz
44chmod +x gordon
45sudo mv gordon /usr/local/bin/
46```
47
48**Linux (ARM64)** - for Raspberry Pi 4, AWS Graviton, Oracle Ampere, etc.
49
50```bash
51wget https://github.com/bnema/gordon/releases/latest/download/{gordon_linux_arm64.tar.gz,checksums.txt}
52grep -E '^[[:xdigit:]]{64} ([* ]?)gordon_linux_arm64\.tar\.gz$' checksums.txt | sha256sum --check --strict
53tar -xzf gordon_linux_arm64.tar.gz
54chmod +x gordon
55sudo mv gordon /usr/local/bin/
56```
57
58**macOS (Apple Silicon)**
59
60```bash
61curl -LO https://github.com/bnema/gordon/releases/latest/download/{gordon_darwin_arm64.tar.gz,checksums.txt}
62grep -E '^[[:xdigit:]]{64} ([* ]?)gordon_darwin_arm64\.tar\.gz$' checksums.txt | shasum -a 256 --check
63tar -xzf gordon_darwin_arm64.tar.gz
64chmod +x gordon
65sudo mv gordon /usr/local/bin/
66```
67
68**macOS (Intel)**
69
70```bash
71curl -LO https://github.com/bnema/gordon/releases/latest/download/{gordon_darwin_amd64.tar.gz,checksums.txt}
72grep -E '^[[:xdigit:]]{64} ([* ]?)gordon_darwin_amd64\.tar\.gz$' checksums.txt | shasum -a 256 --check
73tar -xzf gordon_darwin_amd64.tar.gz
74chmod +x gordon
75sudo mv gordon /usr/local/bin/
76```
77
78Verify installation:
79
80```bash
81gordon version
82```
83
84### From Source
85
86```bash
87# Requires Go 1.21+
88git clone https://github.com/bnema/gordon.git
89cd gordon
90make build
91sudo mv gordon /usr/local/bin/
92```
93
94## Container Runtime Setup
95
96### Docker
97
98```bash
99# Install Docker from your distribution's signed package repository.
100sudo apt update
101sudo apt install docker.io
102
103# Add user to docker group
104sudo usermod -aG docker $USER
105newgrp docker
106
107# Verify
108docker run hello-world
109```
110
111### Podman (Rootless)
112
113Podman rootless mode provides enhanced security by running containers without root privileges.
114
115```bash
116# Install Podman
117sudo apt update
118sudo apt install -y podman
119
120# Enable user namespaces
121echo 'user.max_user_namespaces=28633' | sudo tee -a /etc/sysctl.conf
122sudo sysctl -p
123
124# Setup subuid/subgid
125sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
126
127# Enable Podman socket
128systemctl --user enable --now podman.socket
129
130# Configure insecure localhost registry (required — Podman blocks HTTP registries by default)
131mkdir -p ~/.config/containers/registries.conf.d
132cat > ~/.config/containers/registries.conf.d/gordon.conf <<EOF
133[[registry]]
134location = "localhost:5000"
135insecure = true
136EOF
137```
138
139## Firewall Configuration
140
141Gordon needs ports accessible for the registry and the public edge entrypoint.
142
143### Using firewalld
144
145```bash
146# Install and enable firewalld
147sudo apt install -y firewalld
148sudo systemctl enable --now firewalld
149
150# Allow HTTP/HTTPS
151sudo firewall-cmd --permanent --add-service=http
152sudo firewall-cmd --permanent --add-service=https
153
154# For rootless services, bind entrypoints.edge.address to a high port (for example :9000)
155# and redirect the external TCP edge ports your deployment uses.
156sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=9000
157sudo firewall-cmd --permanent --add-forward-port=port=443:proto=tcp:toport=9000
158
159# Apply changes
160sudo firewall-cmd --reload
161
162# Verify
163sudo firewall-cmd --list-all
164```
165
166### Using ufw
167
168```bash
169sudo ufw allow 80/tcp
170sudo ufw allow 443/tcp
171sudo ufw allow 5000/tcp # Registry port (if accessing directly)
172sudo ufw enable
173```
174
175## Configuration
176
177Gordon creates a default configuration on first run:
178
179```bash
180# Run once to generate config, then stop
181gordon serve
182# Press Ctrl+C
183
184# Edit configuration
185nano ~/.config/gordon/gordon.toml
186```
187
188Minimum required configuration:
189
190```toml
191[server]
192registry_port = 5000
193gordon_domain = "gordon.yourdomain.com"
194
195[entrypoints.edge]
196address = ":443"
197protocol = "smart_tcp"
198
199[routes]
200"app.yourdomain.com" = "myapp:latest"
201```
202
203See [Configuration Reference](./config/index.md) for all options.
204
205## Systemd Service
206
207### User Service (Recommended for Rootless)
208
209```bash
210mkdir -p ~/.config/systemd/user
211cat > ~/.config/systemd/user/gordon.service <<EOF
212[Unit]
213Description=Gordon Container Platform
214After=podman.socket
215
216[Service]
217Type=simple
218Restart=always
219RestartSec=5
220# Required for Podman rootless — tells Gordon where to find the Podman socket
221Environment=XDG_RUNTIME_DIR=/run/user/%U
222Environment=DOCKER_HOST=unix:///run/user/%U/podman/podman.sock
223ExecStart=/usr/local/bin/gordon serve
224
225[Install]
226WantedBy=default.target
227EOF
228
229> **Docker users:** Omit the two `Environment=` lines above — Docker's socket path is set automatically.
230
231# Enable and start
232systemctl --user daemon-reload
233systemctl --user enable --now gordon
234
235# Enable linger (keep service running after logout)
236sudo loginctl enable-linger $USER
237
238# Check status
239systemctl --user status gordon
240```
241
242### System Service (Root Mode)
243
244```bash
245sudo cat > /etc/systemd/system/gordon.service <<EOF
246[Unit]
247Description=Gordon Container Platform
248After=docker.service
249Requires=docker.service
250
251[Service]
252Type=simple
253Restart=always
254RestartSec=5
255ExecStart=/usr/local/bin/gordon serve
256User=root
257
258[Install]
259WantedBy=multi-user.target
260EOF
261
262sudo systemctl daemon-reload
263sudo systemctl enable --now gordon
264```
265
266## DNS Configuration
267
268Point your domains to your server:
269
270| Type | Name | Content | Proxy |
271|------|------|---------|-------|
272| A | `app` | `SERVER_IP` | Yes (Cloudflare) |
273| A | `registry` | `SERVER_IP` | Yes (Cloudflare) |
274
275> **Note:** Enable Cloudflare proxy (orange cloud) for automatic HTTPS. Gordon receives HTTP traffic from Cloudflare.
276
277## Cloudflare SSL Mode
278
279Gordon serves public application traffic through HTTP-capable entrypoints such as `entrypoints.edge` with `protocol = "smart_tcp"`. Choose the Cloudflare mode that matches how you want Cloudflare to reach your origin.
280
281| Mode | How it works | Use when |
282|------|-------------|----------|
283| **Flexible** | Cloudflare terminates TLS; connects to Gordon with cleartext HTTP | You want edge HTTPS only |
284| **Full (Strict)** | Cloudflare connects to Gordon over HTTPS with a valid cert | You want end-to-end HTTPS using Gordon's public ACME certs or a static cert (`tls_cert_file` / `tls_key_file`) |
285
286Wrong mode causes: **521** (Cloudflare can't connect) or **525** (TLS handshake failed).
287
288> **Important:** For Cloudflare-proxied HTTP paths, set `proxy_allowed_ips` with Cloudflare edge IPs — see [Proxy Origin Allowlist](#proxy-origin-allowlist) below.
289>
290> **Rootless note:** Unprivileged users can't bind privileged ports. Bind `entrypoints.edge.address` to a high port (for example `:9000`) and forward/map external ports to it via firewall or container settings.
291
292## Proxy Origin Allowlist
293
294When Gordon serves HTTP paths through a smart TCP edge, direct non-localhost HTTP requests can be restricted to certificate onboarding paths. Cloudflare and other reverse proxies must be listed in `proxy_allowed_ips` to reach your applications.
295
296Add Cloudflare edge IPs to your `gordon.toml`:
297
298```toml
299[server]
300proxy_allowed_ips = [
301 "173.245.48.0/20", "103.21.244.0/22", "103.22.200.0/22",
302 "103.31.4.0/22", "141.101.64.0/18", "108.162.192.0/18",
303 "190.93.240.0/20", "188.114.96.0/20", "197.234.240.0/22",
304 "198.41.128.0/17", "162.158.0.0/15", "104.16.0.0/13",
305 "104.24.0.0/14", "172.64.0.0/13", "131.0.72.0/22",
306]
307```
308
309Without this setting, Cloudflare traffic receives `403 Forbidden: Only certificate onboarding is available over HTTP`.
310
311> **Note:** This is separate from `[api.rate_limit] trusted_proxies`, which controls IP extraction from `X-Forwarded-For`. Both should list your proxy IPs. See [Proxy Origin IP Allowlist](./config/server.md#proxy-origin-ip-allowlist) for details.
312
313## Installing a Specific Version or Pre-Release
314
315```bash
316# Install an exact version
317curl -fsSL https://gordon.bnema.dev/install | GORDON_VERSION=v2.30.1 bash
318
319# Install the latest pre-release instead of the latest stable release
320curl -fsSL https://gordon.bnema.dev/install | GORDON_PRERELEASE=1 bash
321```
322
323By default, the installer resolves `latest` to the newest stable release and verifies the downloaded checksum before installing.
324
325## Verify Installation
326
327```bash
328# Check Gordon is running
329systemctl --user status gordon
330
331# View logs
332journalctl --user -u gordon -f
333
334# Test registry (from local machine)
335docker login registry.yourdomain.com
336docker pull alpine
337docker tag alpine registry.yourdomain.com/test:latest
338docker push registry.yourdomain.com/test:latest
339```
340
341## Data Directories
342
343Gordon stores data in the following locations:
344
345| Path | Purpose |
346|------|---------|
347| `~/.config/gordon/gordon.toml` | Configuration file |
348| `~/.gordon/` | Default data directory |
349| `~/.gordon/registry/` | Container images |
350| `~/.gordon/env/` | Environment files |
351| `~/.gordon/logs/` | Application logs |
352| `~/.gordon/secrets/` | Secrets (unsafe backend only) |
353
354## Related
355
356- [Getting Started](./getting-started.md)
357- [Configuration Reference](./config/index.md)
358- [Podman Rootless Setup](/wiki/guides/podman-rootless.md)