docs: fix misleading frontend example and add container deployment guide
- Clarify that container hostnames only work server-side (SSR/API routes), not in browser JavaScript - Add separate example showing browser clients using public routes - Add new wiki guide for running Gordon in Docker and Podman containers
- commit
- 183e585de9f0cf7a261bc8a772cd42cf3424c163
- author
- brice · · 2026-01-16 06:30 UTC
- parent
- 2dab8de
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +303 -2
M docs/config/network-groups.md +8 -2
| @@ -58,12 +58,18 @@ Applications in the same group can communicate using container names: | |||
| 58 | 58 | "services" = ["app.mydomain.com", "api.mydomain.com"] | |
| 59 | 59 | ``` | |
| 60 | 60 | ||
| 61 | - | The frontend can reach the backend: | |
| 61 | + | Server-side code (SSR, API routes) can reach other containers by name: | |
| 62 | 62 | ```javascript | |
| 63 | - | // In frontend app | |
| 63 | + | // In server-side code (SSR / API route) - NOT browser JavaScript | |
| 64 | 64 | const response = await fetch("http://backend:8080/api/data"); | |
| 65 | 65 | ``` | |
| 66 | 66 | ||
| 67 | + | Browser clients should use the public route instead: | |
| 68 | + | ```javascript | |
| 69 | + | // In browser JavaScript | |
| 70 | + | const response = await fetch("https://api.mydomain.com/api/data"); | |
| 71 | + | ``` | |
| 72 | + | ||
| 67 | 73 | ## Shared Attachments | |
| 68 | 74 | ||
| 69 | 75 | Combine network groups with attachments for shared services: | |
| @@ -58,12 +58,18 @@ Applications in the same group can communicate using container names: | |||||
| 58 | "services" = ["app.mydomain.com", "api.mydomain.com"] | 58 | "services" = ["app.mydomain.com", "api.mydomain.com"] | ||
| 59 | ``` | 59 | ``` | ||
| 60 | 60 | ||||
| 61 | - | The frontend can reach the backend: | 61 | + | Server-side code (SSR, API routes) can reach other containers by name: |
| 62 | ```javascript | 62 | ```javascript | ||
| 63 | - | // In frontend app | 63 | + | // In server-side code (SSR / API route) - NOT browser JavaScript |
| 64 | const response = await fetch("http://backend:8080/api/data"); | 64 | const response = await fetch("http://backend:8080/api/data"); | ||
| 65 | ``` | 65 | ``` | ||
| 66 | 66 | ||||
| 67 | + | Browser clients should use the public route instead: | |||
| 68 | + | ```javascript | |||
| 69 | + | // In browser JavaScript | |||
| 70 | + | const response = await fetch("https://api.mydomain.com/api/data"); | |||
| 71 | + | ``` | |||
| 72 | + | ||||
| 67 | ## Shared Attachments | 73 | ## Shared Attachments | ||
| 68 | 74 | ||||
| 69 | Combine network groups with attachments for shared services: | 75 | Combine network groups with attachments for shared services: | ||
M wiki/guides/index.md +1 -0
| @@ -4,6 +4,7 @@ In-depth guides for specific Gordon setups and integrations. | |||
| 4 | 4 | ||
| 5 | 5 | ## Server Setup | |
| 6 | 6 | ||
| 7 | + | - [Running Gordon in a Container](./running-in-container.md) - Deploy Gordon itself in Docker or Podman | |
| 7 | 8 | - [Podman Rootless Setup](./podman-rootless.md) - Enhanced security with rootless containers | |
| 8 | 9 | ||
| 9 | 10 | ## External Services | |
| @@ -4,6 +4,7 @@ In-depth guides for specific Gordon setups and integrations. | |||||
| 4 | 4 | ||||
| 5 | ## Server Setup | 5 | ## Server Setup | ||
| 6 | 6 | ||||
| 7 | + | - [Running Gordon in a Container](./running-in-container.md) - Deploy Gordon itself in Docker or Podman | |||
| 7 | - [Podman Rootless Setup](./podman-rootless.md) - Enhanced security with rootless containers | 8 | - [Podman Rootless Setup](./podman-rootless.md) - Enhanced security with rootless containers | ||
| 8 | 9 | ||||
| 9 | ## External Services | 10 | ## External Services | ||
A wiki/guides/running-in-container.md +293 -0
| @@ -0,0 +1,293 @@ | |||
| 1 | + | # Running Gordon in a Container | |
| 2 | + | ||
| 3 | + | Run Gordon itself inside a Docker or Podman container for isolated deployments. | |
| 4 | + | ||
| 5 | + | ## What You'll Learn | |
| 6 | + | ||
| 7 | + | - Running Gordon in Docker with socket access | |
| 8 | + | - Running Gordon in Podman (rootless) | |
| 9 | + | - Configuration via environment variables | |
| 10 | + | - Volume management for persistence | |
| 11 | + | ||
| 12 | + | ## Prerequisites | |
| 13 | + | ||
| 14 | + | - Docker or Podman installed on the host | |
| 15 | + | - Domain name pointing to your server | |
| 16 | + | - Basic understanding of container networking | |
| 17 | + | ||
| 18 | + | ## Why Run Gordon in a Container? | |
| 19 | + | ||
| 20 | + | - **Isolation**: Gordon runs in its own environment | |
| 21 | + | - **Portability**: Same setup across different hosts | |
| 22 | + | - **Updates**: Easy version management with image tags | |
| 23 | + | - **Orchestration**: Can be managed by Docker Compose, Kubernetes, etc. | |
| 24 | + | ||
| 25 | + | ## Docker | |
| 26 | + | ||
| 27 | + | ### Quick Start | |
| 28 | + | ||
| 29 | + | ```bash | |
| 30 | + | docker run -d \ | |
| 31 | + | --name gordon \ | |
| 32 | + | -p 80:8080 \ | |
| 33 | + | -p 5000:5000 \ | |
| 34 | + | -v /var/run/docker.sock:/var/run/docker.sock \ | |
| 35 | + | -v gordon-data:/data \ | |
| 36 | + | -e GORDON_SERVER_REGISTRY_DOMAIN=registry.mydomain.com \ | |
| 37 | + | ghcr.io/bnema/gordon:latest | |
| 38 | + | ``` | |
| 39 | + | ||
| 40 | + | ### With Configuration File | |
| 41 | + | ||
| 42 | + | Create `gordon.toml`: | |
| 43 | + | ||
| 44 | + | ```toml | |
| 45 | + | [server] | |
| 46 | + | port = 8080 | |
| 47 | + | registry_port = 5000 | |
| 48 | + | registry_domain = "registry.mydomain.com" | |
| 49 | + | ||
| 50 | + | [routes] | |
| 51 | + | "app.mydomain.com" = "myapp:latest" | |
| 52 | + | "api.mydomain.com" = "myapi:latest" | |
| 53 | + | ||
| 54 | + | [logging] | |
| 55 | + | level = "info" | |
| 56 | + | ``` | |
| 57 | + | ||
| 58 | + | Run with mounted config: | |
| 59 | + | ||
| 60 | + | ```bash | |
| 61 | + | docker run -d \ | |
| 62 | + | --name gordon \ | |
| 63 | + | -p 80:8080 \ | |
| 64 | + | -p 5000:5000 \ | |
| 65 | + | -v /var/run/docker.sock:/var/run/docker.sock \ | |
| 66 | + | -v gordon-data:/data \ | |
| 67 | + | -v $(pwd)/gordon.toml:/etc/gordon/gordon.toml:ro \ | |
| 68 | + | ghcr.io/bnema/gordon:latest | |
| 69 | + | ``` | |
| 70 | + | ||
| 71 | + | ### Docker Compose | |
| 72 | + | ||
| 73 | + | ```yaml | |
| 74 | + | services: | |
| 75 | + | gordon: | |
| 76 | + | image: ghcr.io/bnema/gordon:latest | |
| 77 | + | container_name: gordon | |
| 78 | + | restart: unless-stopped | |
| 79 | + | ports: | |
| 80 | + | - "80:8080" | |
| 81 | + | - "5000:5000" | |
| 82 | + | volumes: | |
| 83 | + | - /var/run/docker.sock:/var/run/docker.sock | |
| 84 | + | - gordon-data:/data | |
| 85 | + | - ./gordon.toml:/etc/gordon/gordon.toml:ro | |
| 86 | + | environment: | |
| 87 | + | - GORDON_LOGGING_LEVEL=info | |
| 88 | + | healthcheck: | |
| 89 | + | test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/health"] | |
| 90 | + | interval: 30s | |
| 91 | + | timeout: 10s | |
| 92 | + | retries: 3 | |
| 93 | + | start_period: 40s | |
| 94 | + | ||
| 95 | + | volumes: | |
| 96 | + | gordon-data: | |
| 97 | + | ``` | |
| 98 | + | ||
| 99 | + | Start with: | |
| 100 | + | ||
| 101 | + | ```bash | |
| 102 | + | docker compose up -d | |
| 103 | + | ``` | |
| 104 | + | ||
| 105 | + | ## Podman | |
| 106 | + | ||
| 107 | + | ### Socket Setup | |
| 108 | + | ||
| 109 | + | Podman uses a different socket path. Enable the Podman socket first: | |
| 110 | + | ||
| 111 | + | ```bash | |
| 112 | + | # For rootless Podman (user service) | |
| 113 | + | systemctl --user enable --now podman.socket | |
| 114 | + | ||
| 115 | + | # Socket path: /run/user/$(id -u)/podman/podman.sock | |
| 116 | + | ``` | |
| 117 | + | ||
| 118 | + | For system-wide (root) Podman: | |
| 119 | + | ||
| 120 | + | ```bash | |
| 121 | + | sudo systemctl enable --now podman.socket | |
| 122 | + | ||
| 123 | + | # Socket path: /run/podman/podman.sock | |
| 124 | + | ``` | |
| 125 | + | ||
| 126 | + | ### Quick Start (Rootless) | |
| 127 | + | ||
| 128 | + | ```bash | |
| 129 | + | podman run -d \ | |
| 130 | + | --name gordon \ | |
| 131 | + | -p 8080:8080 \ | |
| 132 | + | -p 5000:5000 \ | |
| 133 | + | --security-opt label=disable \ | |
| 134 | + | -v /run/user/$(id -u)/podman/podman.sock:/var/run/docker.sock \ | |
| 135 | + | -v gordon-data:/data \ | |
| 136 | + | -e GORDON_SERVER_REGISTRY_DOMAIN=registry.mydomain.com \ | |
| 137 | + | -e DOCKER_HOST=unix:///var/run/docker.sock \ | |
| 138 | + | ghcr.io/bnema/gordon:latest | |
| 139 | + | ``` | |
| 140 | + | ||
| 141 | + | > **Note**: `--security-opt label=disable` is required for socket access with SELinux. | |
| 142 | + | ||
| 143 | + | ### Quick Start (Root/System) | |
| 144 | + | ||
| 145 | + | ```bash | |
| 146 | + | sudo podman run -d \ | |
| 147 | + | --name gordon \ | |
| 148 | + | -p 80:8080 \ | |
| 149 | + | -p 5000:5000 \ | |
| 150 | + | --security-opt label=disable \ | |
| 151 | + | -v /run/podman/podman.sock:/var/run/docker.sock \ | |
| 152 | + | -v gordon-data:/data \ | |
| 153 | + | -e GORDON_SERVER_REGISTRY_DOMAIN=registry.mydomain.com \ | |
| 154 | + | -e DOCKER_HOST=unix:///var/run/docker.sock \ | |
| 155 | + | ghcr.io/bnema/gordon:latest | |
| 156 | + | ``` | |
| 157 | + | ||
| 158 | + | ### Podman Compose (Quadlet) | |
| 159 | + | ||
| 160 | + | Create `~/.config/containers/systemd/gordon.container`: | |
| 161 | + | ||
| 162 | + | ```ini | |
| 163 | + | [Unit] | |
| 164 | + | Description=Gordon Container Platform | |
| 165 | + | After=podman.socket | |
| 166 | + | ||
| 167 | + | [Container] | |
| 168 | + | Image=ghcr.io/bnema/gordon:latest | |
| 169 | + | ContainerName=gordon | |
| 170 | + | PublishPort=8080:8080 | |
| 171 | + | PublishPort=5000:5000 | |
| 172 | + | Volume=/run/user/%U/podman/podman.sock:/var/run/docker.sock | |
| 173 | + | Volume=gordon-data:/data | |
| 174 | + | Environment=GORDON_SERVER_REGISTRY_DOMAIN=registry.mydomain.com | |
| 175 | + | Environment=DOCKER_HOST=unix:///var/run/docker.sock | |
| 176 | + | SecurityLabelDisable=true | |
| 177 | + | ||
| 178 | + | [Service] | |
| 179 | + | Restart=always | |
| 180 | + | ||
| 181 | + | [Install] | |
| 182 | + | WantedBy=default.target | |
| 183 | + | ``` | |
| 184 | + | ||
| 185 | + | Enable and start: | |
| 186 | + | ||
| 187 | + | ```bash | |
| 188 | + | systemctl --user daemon-reload | |
| 189 | + | systemctl --user enable --now gordon | |
| 190 | + | ``` | |
| 191 | + | ||
| 192 | + | ## Configuration Reference | |
| 193 | + | ||
| 194 | + | ### Required Volumes | |
| 195 | + | ||
| 196 | + | | Mount | Purpose | | |
| 197 | + | |-------|---------| | |
| 198 | + | | `/var/run/docker.sock` | Access to container runtime (Docker or Podman socket) | | |
| 199 | + | | `/data` | Registry storage, logs, environment files | | |
| 200 | + | ||
| 201 | + | ### Optional Volumes | |
| 202 | + | ||
| 203 | + | | Mount | Purpose | | |
| 204 | + | |-------|---------| | |
| 205 | + | | `/etc/gordon/gordon.toml` | Configuration file | | |
| 206 | + | ||
| 207 | + | ### Ports | |
| 208 | + | ||
| 209 | + | | Port | Purpose | | |
| 210 | + | |------|---------| | |
| 211 | + | | `8080` | HTTP proxy (map to 80 on host) | | |
| 212 | + | | `5000` | Container registry | | |
| 213 | + | ||
| 214 | + | ### Environment Variables | |
| 215 | + | ||
| 216 | + | All configuration options can be set via environment variables with `GORDON_` prefix: | |
| 217 | + | ||
| 218 | + | | Variable | Description | Example | | |
| 219 | + | |----------|-------------|---------| | |
| 220 | + | | `GORDON_SERVER_PORT` | Proxy port | `8080` | | |
| 221 | + | | `GORDON_SERVER_REGISTRY_PORT` | Registry port | `5000` | | |
| 222 | + | | `GORDON_SERVER_REGISTRY_DOMAIN` | Registry domain (required) | `registry.mydomain.com` | | |
| 223 | + | | `GORDON_SERVER_DATA_DIR` | Data directory | `/data` | | |
| 224 | + | | `GORDON_LOGGING_LEVEL` | Log level | `debug`, `info`, `warn`, `error` | | |
| 225 | + | | `GORDON_REGISTRY_AUTH_ENABLED` | Enable registry auth | `true` | | |
| 226 | + | | `GORDON_REGISTRY_AUTH_USERNAME` | Registry username | `admin` | | |
| 227 | + | | `GORDON_REGISTRY_AUTH_PASSWORD` | Registry password | `secret` | | |
| 228 | + | ||
| 229 | + | ## Security Considerations | |
| 230 | + | ||
| 231 | + | ### Docker Socket Access | |
| 232 | + | ||
| 233 | + | Mounting the Docker/Podman socket grants Gordon full access to the container runtime. This is required for Gordon to manage containers but has security implications: | |
| 234 | + | ||
| 235 | + | - Gordon can create, stop, and remove any container | |
| 236 | + | - Gordon can access volumes and networks | |
| 237 | + | - Consider network policies to restrict Gordon's container access | |
| 238 | + | ||
| 239 | + | ### Recommendations | |
| 240 | + | ||
| 241 | + | 1. **Use a dedicated host**: Run Gordon on a dedicated server or VM | |
| 242 | + | 2. **Limit network exposure**: Only expose ports 80/443 publicly | |
| 243 | + | 3. **Use TLS**: Put Gordon behind a reverse proxy with TLS (Cloudflare, Caddy, nginx) | |
| 244 | + | 4. **Enable registry auth**: Always enable authentication for the registry | |
| 245 | + | ||
| 246 | + | ## Troubleshooting | |
| 247 | + | ||
| 248 | + | ### "Cannot connect to Docker daemon" | |
| 249 | + | ||
| 250 | + | Verify socket mount: | |
| 251 | + | ||
| 252 | + | ```bash | |
| 253 | + | # Docker | |
| 254 | + | docker exec gordon ls -la /var/run/docker.sock | |
| 255 | + | ||
| 256 | + | # Podman - check socket exists | |
| 257 | + | ls -la /run/user/$(id -u)/podman/podman.sock | |
| 258 | + | ``` | |
| 259 | + | ||
| 260 | + | ### "Permission denied" on socket | |
| 261 | + | ||
| 262 | + | For Podman, ensure `--security-opt label=disable` is set. | |
| 263 | + | ||
| 264 | + | For Docker, ensure the `gordon` user inside the container can access the socket: | |
| 265 | + | ||
| 266 | + | ```bash | |
| 267 | + | # Check socket permissions on host | |
| 268 | + | ls -la /var/run/docker.sock | |
| 269 | + | ``` | |
| 270 | + | ||
| 271 | + | ### Container can't bind to port 80 | |
| 272 | + | ||
| 273 | + | The Gordon container runs as non-root user `gordon`. Use port 8080 inside the container and map to 80 on the host: | |
| 274 | + | ||
| 275 | + | ```bash | |
| 276 | + | -p 80:8080 # Host port 80 -> Container port 8080 | |
| 277 | + | ``` | |
| 278 | + | ||
| 279 | + | ### Health check failing | |
| 280 | + | ||
| 281 | + | ```bash | |
| 282 | + | # Check Gordon logs | |
| 283 | + | docker logs gordon | |
| 284 | + | ||
| 285 | + | # Test health endpoint manually | |
| 286 | + | docker exec gordon wget -q -O- http://localhost:8080/health | |
| 287 | + | ``` | |
| 288 | + | ||
| 289 | + | ## Next Steps | |
| 290 | + | ||
| 291 | + | - [Configure routes](/docs/config/routes.md) | |
| 292 | + | - [Set up network groups](/docs/config/network-groups.md) | |
| 293 | + | - [Enable registry authentication](/docs/config/registry-auth.md) | |
| @@ -0,0 +1,293 @@ | |||||
| 1 | + | # Running Gordon in a Container | |||
| 2 | + | ||||
| 3 | + | Run Gordon itself inside a Docker or Podman container for isolated deployments. | |||
| 4 | + | ||||
| 5 | + | ## What You'll Learn | |||
| 6 | + | ||||
| 7 | + | - Running Gordon in Docker with socket access | |||
| 8 | + | - Running Gordon in Podman (rootless) | |||
| 9 | + | - Configuration via environment variables | |||
| 10 | + | - Volume management for persistence | |||
| 11 | + | ||||
| 12 | + | ## Prerequisites | |||
| 13 | + | ||||
| 14 | + | - Docker or Podman installed on the host | |||
| 15 | + | - Domain name pointing to your server | |||
| 16 | + | - Basic understanding of container networking | |||
| 17 | + | ||||
| 18 | + | ## Why Run Gordon in a Container? | |||
| 19 | + | ||||
| 20 | + | - **Isolation**: Gordon runs in its own environment | |||
| 21 | + | - **Portability**: Same setup across different hosts | |||
| 22 | + | - **Updates**: Easy version management with image tags | |||
| 23 | + | - **Orchestration**: Can be managed by Docker Compose, Kubernetes, etc. | |||
| 24 | + | ||||
| 25 | + | ## Docker | |||
| 26 | + | ||||
| 27 | + | ### Quick Start | |||
| 28 | + | ||||
| 29 | + | ```bash | |||
| 30 | + | docker run -d \ | |||
| 31 | + | --name gordon \ | |||
| 32 | + | -p 80:8080 \ | |||
| 33 | + | -p 5000:5000 \ | |||
| 34 | + | -v /var/run/docker.sock:/var/run/docker.sock \ | |||
| 35 | + | -v gordon-data:/data \ | |||
| 36 | + | -e GORDON_SERVER_REGISTRY_DOMAIN=registry.mydomain.com \ | |||
| 37 | + | ghcr.io/bnema/gordon:latest | |||
| 38 | + | ``` | |||
| 39 | + | ||||
| 40 | + | ### With Configuration File | |||
| 41 | + | ||||
| 42 | + | Create `gordon.toml`: | |||
| 43 | + | ||||
| 44 | + | ```toml | |||
| 45 | + | [server] | |||
| 46 | + | port = 8080 | |||
| 47 | + | registry_port = 5000 | |||
| 48 | + | registry_domain = "registry.mydomain.com" | |||
| 49 | + | ||||
| 50 | + | [routes] | |||
| 51 | + | "app.mydomain.com" = "myapp:latest" | |||
| 52 | + | "api.mydomain.com" = "myapi:latest" | |||
| 53 | + | ||||
| 54 | + | [logging] | |||
| 55 | + | level = "info" | |||
| 56 | + | ``` | |||
| 57 | + | ||||
| 58 | + | Run with mounted config: | |||
| 59 | + | ||||
| 60 | + | ```bash | |||
| 61 | + | docker run -d \ | |||
| 62 | + | --name gordon \ | |||
| 63 | + | -p 80:8080 \ | |||
| 64 | + | -p 5000:5000 \ | |||
| 65 | + | -v /var/run/docker.sock:/var/run/docker.sock \ | |||
| 66 | + | -v gordon-data:/data \ | |||
| 67 | + | -v $(pwd)/gordon.toml:/etc/gordon/gordon.toml:ro \ | |||
| 68 | + | ghcr.io/bnema/gordon:latest | |||
| 69 | + | ``` | |||
| 70 | + | ||||
| 71 | + | ### Docker Compose | |||
| 72 | + | ||||
| 73 | + | ```yaml | |||
| 74 | + | services: | |||
| 75 | + | gordon: | |||
| 76 | + | image: ghcr.io/bnema/gordon:latest | |||
| 77 | + | container_name: gordon | |||
| 78 | + | restart: unless-stopped | |||
| 79 | + | ports: | |||
| 80 | + | - "80:8080" | |||
| 81 | + | - "5000:5000" | |||
| 82 | + | volumes: | |||
| 83 | + | - /var/run/docker.sock:/var/run/docker.sock | |||
| 84 | + | - gordon-data:/data | |||
| 85 | + | - ./gordon.toml:/etc/gordon/gordon.toml:ro | |||
| 86 | + | environment: | |||
| 87 | + | - GORDON_LOGGING_LEVEL=info | |||
| 88 | + | healthcheck: | |||
| 89 | + | test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/health"] | |||
| 90 | + | interval: 30s | |||
| 91 | + | timeout: 10s | |||
| 92 | + | retries: 3 | |||
| 93 | + | start_period: 40s | |||
| 94 | + | ||||
| 95 | + | volumes: | |||
| 96 | + | gordon-data: | |||
| 97 | + | ``` | |||
| 98 | + | ||||
| 99 | + | Start with: | |||
| 100 | + | ||||
| 101 | + | ```bash | |||
| 102 | + | docker compose up -d | |||
| 103 | + | ``` | |||
| 104 | + | ||||
| 105 | + | ## Podman | |||
| 106 | + | ||||
| 107 | + | ### Socket Setup | |||
| 108 | + | ||||
| 109 | + | Podman uses a different socket path. Enable the Podman socket first: | |||
| 110 | + | ||||
| 111 | + | ```bash | |||
| 112 | + | # For rootless Podman (user service) | |||
| 113 | + | systemctl --user enable --now podman.socket | |||
| 114 | + | ||||
| 115 | + | # Socket path: /run/user/$(id -u)/podman/podman.sock | |||
| 116 | + | ``` | |||
| 117 | + | ||||
| 118 | + | For system-wide (root) Podman: | |||
| 119 | + | ||||
| 120 | + | ```bash | |||
| 121 | + | sudo systemctl enable --now podman.socket | |||
| 122 | + | ||||
| 123 | + | # Socket path: /run/podman/podman.sock | |||
| 124 | + | ``` | |||
| 125 | + | ||||
| 126 | + | ### Quick Start (Rootless) | |||
| 127 | + | ||||
| 128 | + | ```bash | |||
| 129 | + | podman run -d \ | |||
| 130 | + | --name gordon \ | |||
| 131 | + | -p 8080:8080 \ | |||
| 132 | + | -p 5000:5000 \ | |||
| 133 | + | --security-opt label=disable \ | |||
| 134 | + | -v /run/user/$(id -u)/podman/podman.sock:/var/run/docker.sock \ | |||
| 135 | + | -v gordon-data:/data \ | |||
| 136 | + | -e GORDON_SERVER_REGISTRY_DOMAIN=registry.mydomain.com \ | |||
| 137 | + | -e DOCKER_HOST=unix:///var/run/docker.sock \ | |||
| 138 | + | ghcr.io/bnema/gordon:latest | |||
| 139 | + | ``` | |||
| 140 | + | ||||
| 141 | + | > **Note**: `--security-opt label=disable` is required for socket access with SELinux. | |||
| 142 | + | ||||
| 143 | + | ### Quick Start (Root/System) | |||
| 144 | + | ||||
| 145 | + | ```bash | |||
| 146 | + | sudo podman run -d \ | |||
| 147 | + | --name gordon \ | |||
| 148 | + | -p 80:8080 \ | |||
| 149 | + | -p 5000:5000 \ | |||
| 150 | + | --security-opt label=disable \ | |||
| 151 | + | -v /run/podman/podman.sock:/var/run/docker.sock \ | |||
| 152 | + | -v gordon-data:/data \ | |||
| 153 | + | -e GORDON_SERVER_REGISTRY_DOMAIN=registry.mydomain.com \ | |||
| 154 | + | -e DOCKER_HOST=unix:///var/run/docker.sock \ | |||
| 155 | + | ghcr.io/bnema/gordon:latest | |||
| 156 | + | ``` | |||
| 157 | + | ||||
| 158 | + | ### Podman Compose (Quadlet) | |||
| 159 | + | ||||
| 160 | + | Create `~/.config/containers/systemd/gordon.container`: | |||
| 161 | + | ||||
| 162 | + | ```ini | |||
| 163 | + | [Unit] | |||
| 164 | + | Description=Gordon Container Platform | |||
| 165 | + | After=podman.socket | |||
| 166 | + | ||||
| 167 | + | [Container] | |||
| 168 | + | Image=ghcr.io/bnema/gordon:latest | |||
| 169 | + | ContainerName=gordon | |||
| 170 | + | PublishPort=8080:8080 | |||
| 171 | + | PublishPort=5000:5000 | |||
| 172 | + | Volume=/run/user/%U/podman/podman.sock:/var/run/docker.sock | |||
| 173 | + | Volume=gordon-data:/data | |||
| 174 | + | Environment=GORDON_SERVER_REGISTRY_DOMAIN=registry.mydomain.com | |||
| 175 | + | Environment=DOCKER_HOST=unix:///var/run/docker.sock | |||
| 176 | + | SecurityLabelDisable=true | |||
| 177 | + | ||||
| 178 | + | [Service] | |||
| 179 | + | Restart=always | |||
| 180 | + | ||||
| 181 | + | [Install] | |||
| 182 | + | WantedBy=default.target | |||
| 183 | + | ``` | |||
| 184 | + | ||||
| 185 | + | Enable and start: | |||
| 186 | + | ||||
| 187 | + | ```bash | |||
| 188 | + | systemctl --user daemon-reload | |||
| 189 | + | systemctl --user enable --now gordon | |||
| 190 | + | ``` | |||
| 191 | + | ||||
| 192 | + | ## Configuration Reference | |||
| 193 | + | ||||
| 194 | + | ### Required Volumes | |||
| 195 | + | ||||
| 196 | + | | Mount | Purpose | | |||
| 197 | + | |-------|---------| | |||
| 198 | + | | `/var/run/docker.sock` | Access to container runtime (Docker or Podman socket) | | |||
| 199 | + | | `/data` | Registry storage, logs, environment files | | |||
| 200 | + | ||||
| 201 | + | ### Optional Volumes | |||
| 202 | + | ||||
| 203 | + | | Mount | Purpose | | |||
| 204 | + | |-------|---------| | |||
| 205 | + | | `/etc/gordon/gordon.toml` | Configuration file | | |||
| 206 | + | ||||
| 207 | + | ### Ports | |||
| 208 | + | ||||
| 209 | + | | Port | Purpose | | |||
| 210 | + | |------|---------| | |||
| 211 | + | | `8080` | HTTP proxy (map to 80 on host) | | |||
| 212 | + | | `5000` | Container registry | | |||
| 213 | + | ||||
| 214 | + | ### Environment Variables | |||
| 215 | + | ||||
| 216 | + | All configuration options can be set via environment variables with `GORDON_` prefix: | |||
| 217 | + | ||||
| 218 | + | | Variable | Description | Example | | |||
| 219 | + | |----------|-------------|---------| | |||
| 220 | + | | `GORDON_SERVER_PORT` | Proxy port | `8080` | | |||
| 221 | + | | `GORDON_SERVER_REGISTRY_PORT` | Registry port | `5000` | | |||
| 222 | + | | `GORDON_SERVER_REGISTRY_DOMAIN` | Registry domain (required) | `registry.mydomain.com` | | |||
| 223 | + | | `GORDON_SERVER_DATA_DIR` | Data directory | `/data` | | |||
| 224 | + | | `GORDON_LOGGING_LEVEL` | Log level | `debug`, `info`, `warn`, `error` | | |||
| 225 | + | | `GORDON_REGISTRY_AUTH_ENABLED` | Enable registry auth | `true` | | |||
| 226 | + | | `GORDON_REGISTRY_AUTH_USERNAME` | Registry username | `admin` | | |||
| 227 | + | | `GORDON_REGISTRY_AUTH_PASSWORD` | Registry password | `secret` | | |||
| 228 | + | ||||
| 229 | + | ## Security Considerations | |||
| 230 | + | ||||
| 231 | + | ### Docker Socket Access | |||
| 232 | + | ||||
| 233 | + | Mounting the Docker/Podman socket grants Gordon full access to the container runtime. This is required for Gordon to manage containers but has security implications: | |||
| 234 | + | ||||
| 235 | + | - Gordon can create, stop, and remove any container | |||
| 236 | + | - Gordon can access volumes and networks | |||
| 237 | + | - Consider network policies to restrict Gordon's container access | |||
| 238 | + | ||||
| 239 | + | ### Recommendations | |||
| 240 | + | ||||
| 241 | + | 1. **Use a dedicated host**: Run Gordon on a dedicated server or VM | |||
| 242 | + | 2. **Limit network exposure**: Only expose ports 80/443 publicly | |||
| 243 | + | 3. **Use TLS**: Put Gordon behind a reverse proxy with TLS (Cloudflare, Caddy, nginx) | |||
| 244 | + | 4. **Enable registry auth**: Always enable authentication for the registry | |||
| 245 | + | ||||
| 246 | + | ## Troubleshooting | |||
| 247 | + | ||||
| 248 | + | ### "Cannot connect to Docker daemon" | |||
| 249 | + | ||||
| 250 | + | Verify socket mount: | |||
| 251 | + | ||||
| 252 | + | ```bash | |||
| 253 | + | # Docker | |||
| 254 | + | docker exec gordon ls -la /var/run/docker.sock | |||
| 255 | + | ||||
| 256 | + | # Podman - check socket exists | |||
| 257 | + | ls -la /run/user/$(id -u)/podman/podman.sock | |||
| 258 | + | ``` | |||
| 259 | + | ||||
| 260 | + | ### "Permission denied" on socket | |||
| 261 | + | ||||
| 262 | + | For Podman, ensure `--security-opt label=disable` is set. | |||
| 263 | + | ||||
| 264 | + | For Docker, ensure the `gordon` user inside the container can access the socket: | |||
| 265 | + | ||||
| 266 | + | ```bash | |||
| 267 | + | # Check socket permissions on host | |||
| 268 | + | ls -la /var/run/docker.sock | |||
| 269 | + | ``` | |||
| 270 | + | ||||
| 271 | + | ### Container can't bind to port 80 | |||
| 272 | + | ||||
| 273 | + | The Gordon container runs as non-root user `gordon`. Use port 8080 inside the container and map to 80 on the host: | |||
| 274 | + | ||||
| 275 | + | ```bash | |||
| 276 | + | -p 80:8080 # Host port 80 -> Container port 8080 | |||
| 277 | + | ``` | |||
| 278 | + | ||||
| 279 | + | ### Health check failing | |||
| 280 | + | ||||
| 281 | + | ```bash | |||
| 282 | + | # Check Gordon logs | |||
| 283 | + | docker logs gordon | |||
| 284 | + | ||||
| 285 | + | # Test health endpoint manually | |||
| 286 | + | docker exec gordon wget -q -O- http://localhost:8080/health | |||
| 287 | + | ``` | |||
| 288 | + | ||||
| 289 | + | ## Next Steps | |||
| 290 | + | ||||
| 291 | + | - [Configure routes](/docs/config/routes.md) | |||
| 292 | + | - [Set up network groups](/docs/config/network-groups.md) | |||
| 293 | + | - [Enable registry authentication](/docs/config/registry-auth.md) | |||
M wiki/index.md +1 -0
| @@ -15,6 +15,7 @@ Step-by-step guides for common tasks: | |||
| 15 | 15 | ||
| 16 | 16 | In-depth guides for specific setups: | |
| 17 | 17 | ||
| 18 | + | - [Running Gordon in a Container](./guides/running-in-container.md) - Deploy Gordon itself in Docker or Podman | |
| 18 | 19 | - [Podman Rootless Setup](./guides/podman-rootless.md) - Enhanced security with rootless containers | |
| 19 | 20 | - [Cloudflare DNS & Proxy Setup](./guides/cloudflare-setup.md) - HTTPS termination with Cloudflare | |
| 20 | 21 | - [Using Pass for Secrets](./guides/secrets-pass.md) - Unix password manager integration | |
| @@ -15,6 +15,7 @@ Step-by-step guides for common tasks: | |||||
| 15 | 15 | ||||
| 16 | In-depth guides for specific setups: | 16 | In-depth guides for specific setups: | ||
| 17 | 17 | ||||
| 18 | + | - [Running Gordon in a Container](./guides/running-in-container.md) - Deploy Gordon itself in Docker or Podman | |||
| 18 | - [Podman Rootless Setup](./guides/podman-rootless.md) - Enhanced security with rootless containers | 19 | - [Podman Rootless Setup](./guides/podman-rootless.md) - Enhanced security with rootless containers | ||
| 19 | - [Cloudflare DNS & Proxy Setup](./guides/cloudflare-setup.md) - HTTPS termination with Cloudflare | 20 | - [Cloudflare DNS & Proxy Setup](./guides/cloudflare-setup.md) - HTTPS termination with Cloudflare | ||
| 20 | - [Using Pass for Secrets](./guides/secrets-pass.md) - Unix password manager integration | 21 | - [Using Pass for Secrets](./guides/secrets-pass.md) - Unix password manager integration | ||