detached 705996e

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
705996e6275d50eeaac6d20e880ce6fc78bb8578
author
brice · · 2026-01-16 06:30 UTC
parent
21b89e6
signature
unsigned
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:
5858 "services" = ["app.mydomain.com", "api.mydomain.com"]
5959 ```
6060
61-The frontend can reach the backend:
61+Server-side code (SSR, API routes) can reach other containers by name:
6262 ```javascript
63-// In frontend app
63+// In server-side code (SSR / API route) - NOT browser JavaScript
6464 const response = await fetch("http://backend:8080/api/data");
6565 ```
6666
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+
6773 ## Shared Attachments
6874
6975 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 ```javascript62 ```javascript
63-// In frontend app63+// 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 Attachments73 ## 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.
44
55 ## Server Setup
66
7+- [Running Gordon in a Container](./running-in-container.md) - Deploy Gordon itself in Docker or Podman
78 - [Podman Rootless Setup](./podman-rootless.md) - Enhanced security with rootless containers
89
910 ## External Services
@@ -4,6 +4,7 @@ In-depth guides for specific Gordon setups and integrations.
4 4
5 ## Server Setup5 ## 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 containers8 - [Podman Rootless Setup](./podman-rootless.md) - Enhanced security with rootless containers
8 9
9 ## External Services10 ## 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:
1515
1616 In-depth guides for specific setups:
1717
18+- [Running Gordon in a Container](./guides/running-in-container.md) - Deploy Gordon itself in Docker or Podman
1819 - [Podman Rootless Setup](./guides/podman-rootless.md) - Enhanced security with rootless containers
1920 - [Cloudflare DNS & Proxy Setup](./guides/cloudflare-setup.md) - HTTPS termination with Cloudflare
2021 - [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 containers19 - [Podman Rootless Setup](./guides/podman-rootless.md) - Enhanced security with rootless containers
19 - [Cloudflare DNS & Proxy Setup](./guides/cloudflare-setup.md) - HTTPS termination with Cloudflare20 - [Cloudflare DNS & Proxy Setup](./guides/cloudflare-setup.md) - HTTPS termination with Cloudflare
20 - [Using Pass for Secrets](./guides/secrets-pass.md) - Unix password manager integration21 - [Using Pass for Secrets](./guides/secrets-pass.md) - Unix password manager integration

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close