fix: ensure label port in container bindings, improve docs
- Add labeled proxy port to ContainerConfig.Ports if missing from EXPOSE directives, so Docker creates the host port binding - Update auto-route docs to describe HTTP-port preference fallback - Add propagated image labels subsection to docker-labels reference - Update docker inspect example to show gordon.proxy.port
- commit
- bae2d02b6ad22eb5687e7795fc6f452e792c4fb4
- author
- brice · · 2026-03-15 08:42 UTC
- parent
- 5dc1b42
- signature
- unsigned
3 files · +25 -2
M docs/config/auto-route.md +1 -1
| @@ -203,7 +203,7 @@ When you push an image with Gordon labels: | |||
| 203 | 203 | ||
| 204 | 204 | 1. Gordon reads the `gordon.domains` label | |
| 205 | 205 | 2. Creates routes for each domain automatically | |
| 206 | - | 3. Uses `gordon.proxy.port` for the proxy target (or first EXPOSE port) | |
| 206 | + | 3. Uses `gordon.proxy.port` for the proxy target, otherwise auto-detects an exposed port preferring common HTTP ports | |
| 207 | 207 | 4. Configures health checks if `gordon.health` is set | |
| 208 | 208 | 5. Extracts environment variables from `gordon.env-file` if specified | |
| 209 | 209 | ||
| @@ -203,7 +203,7 @@ When you push an image with Gordon labels: | |||||
| 203 | 203 | ||||
| 204 | 1. Gordon reads the `gordon.domains` label | 204 | 1. Gordon reads the `gordon.domains` label | ||
| 205 | 2. Creates routes for each domain automatically | 205 | 2. Creates routes for each domain automatically | ||
| 206 | - | 3. Uses `gordon.proxy.port` for the proxy target (or first EXPOSE port) | 206 | + | 3. Uses `gordon.proxy.port` for the proxy target, otherwise auto-detects an exposed port preferring common HTTP ports |
| 207 | 4. Configures health checks if `gordon.health` is set | 207 | 4. Configures health checks if `gordon.health` is set | ||
| 208 | 5. Extracts environment variables from `gordon.env-file` if specified | 208 | 5. Extracts environment variables from `gordon.env-file` if specified | ||
| 209 | 209 | ||||
M docs/reference/docker-labels.md +12 -0
| @@ -14,6 +14,17 @@ Gordon adds these labels to managed containers: | |||
| 14 | 14 | | `gordon.route` | Domain name | Route this container handles | | |
| 15 | 15 | | `gordon.created` | Timestamp | When Gordon created the container | | |
| 16 | 16 | ||
| 17 | + | ### Propagated Image Labels | |
| 18 | + | ||
| 19 | + | When an image defines these labels, Gordon copies them onto the container for runtime use (readiness probing, proxy routing): | |
| 20 | + | ||
| 21 | + | | Label | Example | Description | | |
| 22 | + | |-------|---------|-------------| | |
| 23 | + | | `gordon.proxy.port` | `"3000"` | Port the proxy routes HTTP traffic to | | |
| 24 | + | | `gordon.health` | `"/healthz"` | Health check endpoint for readiness probing | | |
| 25 | + | ||
| 26 | + | These labels only appear on containers whose source image defined them. | |
| 27 | + | ||
| 17 | 28 | ### Attachment Labels | |
| 18 | 29 | ||
| 19 | 30 | Additional labels for attachment containers: | |
| @@ -107,6 +118,7 @@ Example output: | |||
| 107 | 118 | "gordon.domain": "app.mydomain.com", | |
| 108 | 119 | "gordon.image": "myapp:latest", | |
| 109 | 120 | "gordon.managed": "true", | |
| 121 | + | "gordon.proxy.port": "3000", | |
| 110 | 122 | "gordon.route": "app.mydomain.com" | |
| 111 | 123 | } | |
| 112 | 124 | ``` | |
| @@ -14,6 +14,17 @@ Gordon adds these labels to managed containers: | |||||
| 14 | | `gordon.route` | Domain name | Route this container handles | | 14 | | `gordon.route` | Domain name | Route this container handles | | ||
| 15 | | `gordon.created` | Timestamp | When Gordon created the container | | 15 | | `gordon.created` | Timestamp | When Gordon created the container | | ||
| 16 | 16 | ||||
| 17 | + | ### Propagated Image Labels | |||
| 18 | + | ||||
| 19 | + | When an image defines these labels, Gordon copies them onto the container for runtime use (readiness probing, proxy routing): | |||
| 20 | + | ||||
| 21 | + | | Label | Example | Description | | |||
| 22 | + | |-------|---------|-------------| | |||
| 23 | + | | `gordon.proxy.port` | `"3000"` | Port the proxy routes HTTP traffic to | | |||
| 24 | + | | `gordon.health` | `"/healthz"` | Health check endpoint for readiness probing | | |||
| 25 | + | ||||
| 26 | + | These labels only appear on containers whose source image defined them. | |||
| 27 | + | ||||
| 17 | ### Attachment Labels | 28 | ### Attachment Labels | ||
| 18 | 29 | ||||
| 19 | Additional labels for attachment containers: | 30 | Additional labels for attachment containers: | ||
| @@ -107,6 +118,7 @@ Example output: | |||||
| 107 | "gordon.domain": "app.mydomain.com", | 118 | "gordon.domain": "app.mydomain.com", | ||
| 108 | "gordon.image": "myapp:latest", | 119 | "gordon.image": "myapp:latest", | ||
| 109 | "gordon.managed": "true", | 120 | "gordon.managed": "true", | ||
| 121 | + | "gordon.proxy.port": "3000", | |||
| 110 | "gordon.route": "app.mydomain.com" | 122 | "gordon.route": "app.mydomain.com" | ||
| 111 | } | 123 | } | ||
| 112 | ``` | 124 | ``` | ||
M internal/usecase/container/service.go +12 -1
| @@ -241,10 +241,21 @@ func (s *Service) buildContainerConfig(in containerConfigInput) *domain.Containe | |||
| 241 | 241 | } | |
| 242 | 242 | } | |
| 243 | 243 | ||
| 244 | + | // Ensure the label-specified proxy port is included in exposed ports | |
| 245 | + | // so Docker creates a host port binding for it. | |
| 246 | + | ports := in.ExposedPorts | |
| 247 | + | if portStr, ok := labels[domain.LabelProxyPort]; ok { | |
| 248 | + | if port, err := strconv.Atoi(portStr); err == nil && port > 0 && port <= 65535 { | |
| 249 | + | if !slices.Contains(ports, port) { | |
| 250 | + | ports = append(ports, port) | |
| 251 | + | } | |
| 252 | + | } | |
| 253 | + | } | |
| 254 | + | ||
| 244 | 255 | return &domain.ContainerConfig{ | |
| 245 | 256 | Image: in.ImageRef, | |
| 246 | 257 | Name: containerName, | |
| 247 | - | Ports: in.ExposedPorts, | |
| 258 | + | Ports: ports, | |
| 248 | 259 | Env: in.EnvVars, | |
| 249 | 260 | Volumes: in.Volumes, | |
| 250 | 261 | NetworkMode: in.NetworkName, | |
| @@ -241,10 +241,21 @@ func (s *Service) buildContainerConfig(in containerConfigInput) *domain.Containe | |||||
| 241 | } | 241 | } | ||
| 242 | } | 242 | } | ||
| 243 | 243 | ||||
| 244 | + | // Ensure the label-specified proxy port is included in exposed ports | |||
| 245 | + | // so Docker creates a host port binding for it. | |||
| 246 | + | ports := in.ExposedPorts | |||
| 247 | + | if portStr, ok := labels[domain.LabelProxyPort]; ok { | |||
| 248 | + | if port, err := strconv.Atoi(portStr); err == nil && port > 0 && port <= 65535 { | |||
| 249 | + | if !slices.Contains(ports, port) { | |||
| 250 | + | ports = append(ports, port) | |||
| 251 | + | } | |||
| 252 | + | } | |||
| 253 | + | } | |||
| 254 | + | ||||
| 244 | return &domain.ContainerConfig{ | 255 | return &domain.ContainerConfig{ | ||
| 245 | Image: in.ImageRef, | 256 | Image: in.ImageRef, | ||
| 246 | Name: containerName, | 257 | Name: containerName, | ||
| 247 | - | Ports: in.ExposedPorts, | 258 | + | Ports: ports, |
| 248 | Env: in.EnvVars, | 259 | Env: in.EnvVars, | ||
| 249 | Volumes: in.Volumes, | 260 | Volumes: in.Volumes, | ||
| 250 | NetworkMode: in.NetworkName, | 261 | NetworkMode: in.NetworkName, | ||