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