# Server Configuration Core server settings for Gordon. ## Configuration ```toml [server] port = 8088 # HTTP proxy port registry_port = 5000 # Container registry port tls_port = 8443 # HTTPS listener port (0 = disabled) # tls_cert_file = "" # Optional: PEM certificate for static TLS # tls_key_file = "" # Optional: PEM key for static TLS # force_https_redirect = false # Redirect all HTTP traffic to HTTPS gordon_domain = "gordon.mydomain.com" # Gordon domain (required) # data_dir = "~/.gordon" # Data storage directory (default) ``` ## Options | Option | Type | Default | Description | |--------|------|---------|-------------| | `port` | int | `8088` | HTTP proxy port for routing traffic to containers | | `registry_port` | int | `5000` | Docker registry port for image push/pull | | `tls_port` | int | `8443` | HTTPS listener port with internal CA. Set to `0` to disable TLS entirely | | `tls_cert_file` | string | `""` | Path to PEM certificate file. When set (with `tls_key_file`), this cert is served for matching SNI domains; unmatched domains fall through to the internal CA | | `tls_key_file` | string | `""` | Path to PEM private key file. Must be set together with `tls_cert_file` | | `force_https_redirect` | bool | `false` | Redirect all HTTP requests to the HTTPS port. For direct-access setups without a TLS-terminating proxy | | `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) | ## Port Configuration ### Proxy Port The `port` setting controls where Gordon listens for HTTP traffic: ```toml [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: ```bash # Forward port 80 to 8080 sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080 ``` ### Internal CA and TLS Gordon includes an internal certificate authority that issues on-demand TLS certificates for proxied domains. The HTTPS listener is enabled by default on `tls_port` (8443). ```toml [server] tls_port = 8443 # Default. Set to 0 to disable TLS entirely. ``` With TLS enabled: - Gordon runs an internal CA (root + intermediate) stored in `{data_dir}/pki/` - Leaf certificates are issued on-demand per domain (SNI-based) and cached in memory - The intermediate CA auto-renews before expiry - An onboarding page at `https://:/ca` lets clients download the root CA certificate - The root CA is stable across restarts (generated once, persisted to disk) #### Custom certificates To use your own certificate (e.g. from Tailscale, Let's Encrypt, or a corporate CA) alongside the internal CA: ```toml [server] tls_cert_file = "/etc/gordon/tls/cert.pem" tls_key_file = "/etc/gordon/tls/key.pem" ``` The static certificate is served for SNI-matching domains (including wildcards). All other domains get on-demand certs from the internal CA. #### HTTP to HTTPS redirect When clients connect directly (no Cloudflare or reverse proxy in front), enable `force_https_redirect` to redirect all HTTP traffic to the HTTPS port: ```toml [server] force_https_redirect = true ``` When `proxy_allowed_ips` is configured, non-proxy clients are redirected automatically even without this flag — trusted proxy IPs pass through to serve Cloudflare-proxied traffic. #### Disabling TLS For setups where TLS is handled entirely by an external proxy (Cloudflare, nginx, etc.): ```toml [server] tls_port = 0 # No HTTPS listener, no internal CA generated ``` #### Firewall port forwarding For rootless user services, forward 443 to the high port at the firewall: ```bash # Forward 443 -> 8443 sudo firewall-cmd --permanent --add-forward-port=port=443:proto=tcp:toport=8443 sudo firewall-cmd --reload ``` With firewalld and Tailscale: ```bash # 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: ```toml [server] registry_port = 5000 ``` Docker/Podman clients push to this port: ```bash 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](../reference/troubleshooting.md#server-gave-http-response-to-https-client) for Docker and Podman instructions. ## Gordon Domain The `gordon_domain` is required and must match your DNS configuration: ```toml [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/*` on `registry_port` is blocked for non-loopback clients. - Admin API traffic should go through the main proxy listener (`server.port`/`server.tls_port`). > **Note:** `registry_domain` is supported as a deprecated alias for backwards compatibility. ## Data Directory The `data_dir` setting controls where Gordon stores data: ```toml [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: ```toml [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:** ```toml 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 `1GB` or higher - CI/CD artifact uploads: adjust based on your largest artifacts - Tight security: reduce to `100MB` or 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/*`): ```toml [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:** ```toml 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). ```toml [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_proxies` for 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: ```bash 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. ```toml [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`), not `X-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](https://www.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. ```toml [server] registry_listen_address = "127.0.0.1" # Loopback only ``` ## HSTS HSTS headers are sent automatically on the HTTPS listener (when `r.TLS != nil`). When Gordon runs behind a TLS-terminating proxy (Cloudflare, Tailscale) and only receives plain HTTP, HSTS is not sent — the edge proxy is expected to handle it. ## Examples ### Development Configuration ```toml [server] port = 8080 registry_port = 5000 gordon_domain = "gordon.local" data_dir = "./dev-data" ``` ### Production Configuration ```toml [server] port = 8080 registry_port = 5000 gordon_domain = "gordon.company.com" data_dir = "/var/lib/gordon" ``` ### Rootless Container Setup ```toml [server] port = 8080 # High port for rootless registry_port = 5000 gordon_domain = "gordon.mydomain.com" ``` With firewall port forwarding: ```bash sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080 sudo firewall-cmd --reload ``` ## Related - [Configuration Overview](./index.md) - [Installation Guide](../installation.md) - [Routes Configuration](./routes.md)