Server Configuration
Core server settings for Gordon.
Configuration
[server]
port = 8080 # HTTP proxy port
registry_port = 5000 # Container registry port
tls_enabled = false # Enable native TLS listener
tls_port = 443 # HTTPS listener port
tls_cert_file = "" # PEM certificate file (required when TLS enabled)
tls_key_file = "" # PEM key file (required when TLS enabled)
gordon_domain = "gordon.mydomain.com" # Gordon domain (required)
# data_dir = "~/.gordon" # Data storage directory (default)
Options
| Option | Type | Default | Description |
|---|---|---|---|
port |
int | 80 |
HTTP proxy port for routing traffic to containers |
registry_port |
int | 5000 |
Docker registry port for image push/pull |
tls_enabled |
bool | false |
Enable native HTTPS listener for proxy traffic |
tls_port |
int | 443 |
HTTPS listener port when tls_enabled is true |
tls_cert_file |
string | "" |
Path to PEM certificate file |
tls_key_file |
string | "" |
Path to PEM private key file |
gordon_domain |
string | required | Domain for Gordon (registry + admin API) |
registry_domain |
string | - | Deprecated alias for gordon_domain |
data_dir |
string | ~/.gordon |
Directory for registry data, logs, and env files |
max_proxy_body_size |
string | "512MB" |
Maximum request body size for proxied requests |
max_blob_chunk_size |
string | "95MB" |
Maximum request body size for a single registry blob upload chunk |
registry_allowed_ips |
[]string | [] |
IPs or CIDR ranges allowed to access the registry (empty = allow all) |
proxy_allowed_ips |
[]string | [] |
IPs or CIDR ranges allowed to reach the proxy (empty = allow all) |
registry_listen_address |
string | "" |
Bind address for registry (empty = all interfaces) |
force_hsts |
bool | false |
Always send HSTS header (for TLS-terminating proxy setups) |
Port Configuration
Proxy Port
The port setting controls where Gordon listens for HTTP traffic:
[server]
port = 8080 # Use 8080 for rootless containers with port forwarding
For rootless containers, you'll typically use a high port and configure firewall port forwarding:
# Forward port 80 to 8080
sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080
Native TLS Listener
Enable native HTTPS directly in Gordon:
[server]
tls_enabled = true
tls_port = 443
tls_cert_file = "/etc/gordon/certs/fullchain.pem"
tls_key_file = "/etc/gordon/certs/privkey.pem"
With TLS enabled:
- Gordon terminates HTTPS on
tls_port - Host-based routing still works (apps,
/v2/*,/auth/*,/admin/*) portremains available for plain HTTP unless you restrict it externally- If
tls_cert_file/tls_key_fileare empty, Gordon auto-generates a self-signed pair in{data_dir}/tls/
For rootless user services, do not bind directly to 443. Use an unprivileged TLS port (for example 8443) and forward 443 to it at the firewall or edge proxy.
[server]
tls_enabled = true
tls_port = 8443
With firewalld and Tailscale:
# Verify tailscale0 is in trusted zone
sudo firewall-cmd --get-zone-of-interface=tailscale0
# If needed, place tailscale0 in trusted
sudo firewall-cmd --permanent --zone=trusted --add-interface=tailscale0
sudo firewall-cmd --reload
# Forward 443 -> 8443 permanently (IPv4 + IPv6)
sudo firewall-cmd --permanent --zone=trusted --add-rich-rule='rule family=ipv4 forward-port port=443 protocol=tcp to-port=8443'
sudo firewall-cmd --permanent --zone=trusted --add-rich-rule='rule family=ipv6 forward-port port=443 protocol=tcp to-port=8443'
sudo firewall-cmd --reload
Registry Port
The registry_port setting controls where Gordon accepts image pushes:
[server]
registry_port = 5000
Docker/Podman clients push to this port:
docker push registry.mydomain.com:5000/myapp:latest
If the registry is served over plain HTTP (no TLS), clients must be configured to allow insecure access. See Troubleshooting: insecure registry for Docker and Podman instructions.
Gordon Domain
The gordon_domain is required and must match your DNS configuration:
[server]
gordon_domain = "gordon.mydomain.com"
This domain is used for:
- Docker login and image push/pull operations
- Admin API access (
/admin/*endpoints) - CLI remote targeting (
gordon routes --remote https://gordon.mydomain.com) - Authentication endpoints (
/auth/*)
When requests arrive on the proxy port with this domain as the Host header, Gordon routes them to the backend services (registry and admin API).
Security note:
- Direct access to
/admin/*onregistry_portis blocked for non-loopback clients. - Admin API traffic should go through the main proxy listener (
server.port/server.tls_port).
Note:
registry_domainis supported as a deprecated alias for backwards compatibility.
Data Directory
The data_dir setting controls where Gordon stores data:
[server]
data_dir = "~/.gordon" # Default for user installations
Directory Structure
~/.gordon/
├── registry/ # Container images and manifests
│ ├── blobs/
│ └── manifests/
├── env/ # Environment files per domain
│ ├── app_mydomain_com.env
│ └── api_mydomain_com.env
├── logs/ # Application and container logs
│ ├── gordon.log
│ ├── proxy.log
│ └── containers/
└── secrets/ # Secrets (unsafe backend only)
Permissions
Gordon creates directories with secure permissions:
- Directories:
0700(owner only) - Files:
0600(owner only)
Maximum Proxy Body Size
The max_proxy_body_size setting limits the size of request bodies that Gordon will forward to backend containers:
[server]
max_proxy_body_size = "512MB" # Default
Purpose: Prevents resource exhaustion attacks from extremely large uploads through the proxy.
Supported units: B, KB, MB, GB, TB (case-insensitive, decimal/1024-based)
Examples:
max_proxy_body_size = "100MB" # Stricter limit
max_proxy_body_size = "1GB" # Allow larger uploads
max_proxy_body_size = "0" # No limit (not recommended)
Behavior:
- When a request exceeds the limit, Gordon returns
413 Request Entity Too Large - The limit is applied before the request reaches the backend container
- Uploads in progress are terminated when the limit is exceeded
Use cases:
- File upload services: increase to
1GBor higher - CI/CD artifact uploads: adjust based on your largest artifacts
- Tight security: reduce to
100MBor lower - Default (512MB): suitable for most applications
Warning: Setting this to
0(no limit) allows unlimited upload sizes, which can lead to disk exhaustion and DoS vulnerabilities. Always set a reasonable limit for production deployments.
Maximum Registry Blob Chunk Size
The max_blob_chunk_size setting limits the size of a single blob upload request to the registry (PATCH/PUT under /v2/*/blobs/uploads/*):
[server]
max_blob_chunk_size = "95MB" # Default
Purpose: Prevents excessive memory usage during blob uploads while allowing large real-world image layers.
Supported units: B, KB, MB, GB, TB (case-insensitive, decimal/1024-based)
Examples:
max_blob_chunk_size = "256MB" # Stricter limit
max_blob_chunk_size = "1GB" # Allow larger layers
Behavior:
- When a blob chunk exceeds the limit, Gordon returns
413 Request Entity Too Large - Gordon CLI uploads image layers in 50MB chunks by default, and this setting caps the maximum chunk accepted per request
- Keep this below Cloudflare's 100MB per-request limit when operating behind Cloudflare; the default (
95MB) is fine - Increase it only if you expect larger direct registry uploads from other clients
Registry IP Allowlist
The registry_allowed_ips setting restricts registry access to specific IPs or IP ranges. Accepts both CIDR notation (100.64.0.0/10) and individual IPs (203.0.113.50). When set, only requests from listed addresses (plus localhost) can reach registry and auth endpoints. An empty list allows all traffic (default).
[server]
registry_allowed_ips = [
"100.64.0.0/10", # Tailscale CGNAT range
"203.0.113.50", # single IP (treated as /32)
]
Behavior:
- Single IPs are automatically converted to
/32(IPv4) or/128(IPv6) - Denied requests receive
403 Forbidden - Localhost (
127.0.0.0/8,::1) is always allowed so Gordon can pull from its own registry - Respects
trusted_proxiesfor accurate client IP extraction behind reverse proxies - Applied before rate limiting and authentication
- Changes require a Gordon server restart; not picked up by config hot-reload
Use cases:
- Restrict registry to Tailscale network:
["100.64.0.0/10"] - Restrict to private subnets:
["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] - Allow a specific CI server:
["100.64.0.0/10", "203.0.113.50"]
Via environment variable:
GORDON_SERVER_REGISTRY_ALLOWED_IPS=100.64.0.0/10
Proxy Origin IP Allowlist
The proxy_allowed_ips setting restricts which IPs can reach the proxy server. This prevents direct-to-origin attacks that bypass CDN protections (e.g. Cloudflare WAF, DDoS mitigation). When set, only connections from listed CIDRs (plus localhost) are accepted.
[server]
# Only accept proxy traffic from Cloudflare edge IPs
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",
]
Behavior:
- Checks the direct connection IP (
RemoteAddr), notX-Forwarded-For - Localhost is always allowed
- Denied requests receive
403 Forbidden - An empty list allows all traffic (default)
Important: Cloudflare publishes their IP ranges at cloudflare.com/ips. Keep your allowlist updated when Cloudflare adds new ranges.
Registry Listen Address
The registry_listen_address controls which interface the registry server binds to. By default it binds to all interfaces. Set to 127.0.0.1 to restrict registry access to localhost only, preventing containers from reaching the registry via the Docker bridge gateway.
[server]
registry_listen_address = "127.0.0.1" # Loopback only
Force HSTS
When Gordon runs behind a TLS-terminating proxy (e.g. Cloudflare), r.TLS is nil so the HSTS header is not sent by default. Enable force_hsts to always include the Strict-Transport-Security header.
[server]
force_hsts = true
Examples
Development Configuration
[server]
port = 8080
registry_port = 5000
gordon_domain = "gordon.local"
data_dir = "./dev-data"
Production Configuration
[server]
port = 8080
registry_port = 5000
gordon_domain = "gordon.company.com"
data_dir = "/var/lib/gordon"
Rootless Container Setup
[server]
port = 8080 # High port for rootless
registry_port = 5000
gordon_domain = "gordon.mydomain.com"
With firewall port forwarding:
sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080
sudo firewall-cmd --reload