Attachments Configuration
Attach service dependencies (databases, caches, queues) to your applications.
Requirements
Network isolation must be enabled for attachments to work properly:
[network_isolation]
enabled = true
Without network isolation, containers run on Docker's default bridge network which does not provide DNS resolution. Your application won't be able to reach attachments by hostname (e.g., postgres:5432).
Configuration
[attachments]
"app.mydomain.com" = ["postgres:latest", "redis:latest"]
"api.mydomain.com" = ["postgres:latest"]
Syntax
[attachments]
"<domain-or-group>" = ["<image>:<tag>", "<image>:<tag>"]
| Component | Description |
|---|---|
domain-or-group |
Route domain or network group name |
image:tag |
Service images to deploy alongside the app |
How Attachments Work
When you define attachments:
- Gordon deploys attachment containers to the same network as your app
- Services are accessible by their image name (before the colon)
- Attachments start before your main application
- Attachments persist across app updates
┌───────────────────────────────────────────────────┐
│ Network: gordon-app-mydomain-com │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ App │───>│ postgres │ │ redis │ │
│ │ :3000 │ │ :5432 │ │ :6379 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
└───────────────────────────────────────────────────┘
Service Discovery
Attachments are accessible by their image name within the network:
[attachments]
"app.mydomain.com" = ["postgres:latest", "redis:latest"]
In your application:
// Connect using simple hostnames
const db = await pg.connect("postgresql://postgres:5432/mydb");
const cache = await redis.connect("redis://redis:6379");
Persistent Storage
Use Dockerfile VOLUME directives for persistent data:
# postgres.Dockerfile
FROM postgres:18
VOLUME ["/var/lib/postgresql/data"]
ENV POSTGRES_DB=myapp
ENV POSTGRES_USER=app
# Password is injected via attachment secrets — do NOT hardcode here
Configure credentials via attachment secrets instead of hardcoding them:
# Set database password securely
gordon secrets set app.mydomain.com --attachment postgres POSTGRES_PASSWORD=secret
# Verify
gordon secrets list app.mydomain.com
This works with all secrets backends (pass, sops, unsafe). See Secrets Configuration for backend details.
Build and push to Gordon:
docker build -f postgres.Dockerfile -t my-postgres:latest .
docker push registry.mydomain.com/my-postgres:latest
Use in attachments:
[attachments]
"app.mydomain.com" = ["my-postgres:latest"]
Attachment Secrets
Inject environment variables into attachment containers using the --attachment flag on secrets commands:
# Set secrets for the postgres attachment
gordon secrets set app.mydomain.com --attachment postgres POSTGRES_USER=admin POSTGRES_PASSWORD=secret
# Set secrets for the redis attachment
gordon secrets set app.mydomain.com --attachment redis REDIS_PASSWORD=cache-secret
# View all secrets (domain + attachments) in a tree view
gordon secrets list app.mydomain.com
# Remove an attachment secret
gordon secrets remove app.mydomain.com --attachment postgres POSTGRES_PASSWORD
The --attachment flag takes the service name (the image name before the colon in your attachments config). For example, if your config has "app.mydomain.com" = ["postgres:18", "redis:7-alpine"], the service names are postgres and redis.
Storage depends on your secrets backend:
- pass:
gordon/env/attachments/<container-name>/<KEY> - sops/unsafe:
gordon-<container-name>.envfiles
See Secrets Configuration and Secrets Commands for details.
Shared Attachments with Network Groups
Share services between multiple apps using network groups:
[network_groups]
"backend" = ["app.mydomain.com", "api.mydomain.com"]
[attachments]
"backend" = ["shared-postgres:latest", "shared-redis:latest"]
Both app.mydomain.com and api.mydomain.com can access the shared services.
Per-App vs Shared Attachments
Per-App Attachments
Each app gets its own isolated service instances:
[attachments]
"app.mydomain.com" = ["postgres:latest"] # App's own postgres
"api.mydomain.com" = ["postgres:latest"] # API's own postgres
Shared Attachments
Multiple apps share the same service instances:
[network_groups]
"backend" = ["app.mydomain.com", "api.mydomain.com"]
[attachments]
"backend" = ["postgres:latest", "redis:latest"] # Shared by both
Mixed Approach
[network_groups]
"backend" = ["app.mydomain.com", "api.mydomain.com"]
[attachments]
"backend" = ["redis:latest"] # Shared cache
"app.mydomain.com" = ["postgres:latest"] # App's own database
"api.mydomain.com" = ["postgres:latest"] # API's own database
Examples
Web App with Database
[routes]
"app.mydomain.com" = "myapp:latest"
[attachments]
"app.mydomain.com" = ["postgres:18", "redis:7-alpine"]
Microservices with Shared Queue
[routes]
"orders.mydomain.com" = "orders-service:latest"
"inventory.mydomain.com" = "inventory-service:latest"
"notifications.mydomain.com" = "notifications-service:latest"
[network_groups]
"services" = ["orders.mydomain.com", "inventory.mydomain.com", "notifications.mydomain.com"]
[attachments]
"services" = ["rabbitmq:3-management"]
"orders.mydomain.com" = ["orders-db:latest"]
"inventory.mydomain.com" = ["inventory-db:latest"]
Custom Service Images
Build custom service images with your configuration:
# my-postgres.Dockerfile
FROM postgres:18
VOLUME ["/var/lib/postgresql/data"]
ENV POSTGRES_DB=production
ENV POSTGRES_USER=appuser
# Password should come from env file
# my-redis.Dockerfile
FROM redis:7-alpine
VOLUME ["/data"]
CMD ["redis-server", "--appendonly", "yes"]
[attachments]
"app.mydomain.com" = ["my-postgres:latest", "my-redis:latest"]
Container Naming
Attachment containers are named:
gordon-<domain>-<image-name>- Example:
gordon-app-mydomain-com-postgres
Labels
Gordon adds labels to attachment containers:
| Label | Value |
|---|---|
gordon.managed |
true |
gordon.attachment |
true |
gordon.attached-to |
Domain or group name |
CLI Management
Manage attachments via the CLI without editing configuration files.
List Attachments
# List all attachments
gordon attachments list
# List attachments for a specific domain or network group
gordon attachments list app.mydomain.com
gordon attachments list backend
# Remote mode
gordon attachments list --remote https://gordon.mydomain.com --token $TOKEN
Add Attachments
# Add attachment to a domain
gordon attachments add app.mydomain.com postgres:18
# Add attachment to a network group
gordon attachments add backend redis:7-alpine
# Remote mode
gordon attachments add app.mydomain.com postgres:18 --remote https://gordon.mydomain.com --token $TOKEN
Remove Attachments
# Remove attachment from a domain
gordon attachments remove app.mydomain.com postgres:18
# Remove from network group
gordon attachments remove backend redis:7-alpine
# Remote mode
gordon attachments remove app.mydomain.com postgres:18 --remote https://gordon.mydomain.com --token $TOKEN
Alias
The gordon attach command is an alias for gordon attachments:
gordon attach list
gordon attach add app.mydomain.com postgres:18
gordon attach remove app.mydomain.com postgres:18