v2.10.0 893d8fa

attachments.md RawHistoryFolder

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:

  1. Gordon deploys attachment containers to the same network as your app
  2. Services are accessible by their image name (before the colon)
  3. Attachments start before your main application
  4. 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
ENV POSTGRES_PASSWORD=secret

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"]

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
source
1# Attachments Configuration
2
3Attach service dependencies (databases, caches, queues) to your applications.
4
5## Requirements
6
7**Network isolation must be enabled** for attachments to work properly:
8
9```toml
10[network_isolation]
11enabled = true
12```
13
14Without 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`).
15
16## Configuration
17
18```toml
19[attachments]
20"app.mydomain.com" = ["postgres:latest", "redis:latest"]
21"api.mydomain.com" = ["postgres:latest"]
22```
23
24## Syntax
25
26```toml
27[attachments]
28"<domain-or-group>" = ["<image>:<tag>", "<image>:<tag>"]
29```
30
31| Component | Description |
32|-----------|-------------|
33| `domain-or-group` | Route domain or network group name |
34| `image:tag` | Service images to deploy alongside the app |
35
36## How Attachments Work
37
38When you define attachments:
39
401. Gordon deploys attachment containers to the same network as your app
412. Services are accessible by their image name (before the colon)
423. Attachments start before your main application
434. Attachments persist across app updates
44
45```
46┌───────────────────────────────────────────────────┐
47│ Network: gordon-app-mydomain-com                  │
48│                                                   │
49│  ┌──────────┐    ┌──────────┐    ┌──────────┐     │
50│  │   App    │───>│ postgres │    │  redis   │     │
51│  │  :3000   │    │  :5432   │    │  :6379   │     │
52│  └──────────┘    └──────────┘    └──────────┘     │
53│                                                   │
54└───────────────────────────────────────────────────┘
55```
56
57## Service Discovery
58
59Attachments are accessible by their image name within the network:
60
61```toml
62[attachments]
63"app.mydomain.com" = ["postgres:latest", "redis:latest"]
64```
65
66In your application:
67```javascript
68// Connect using simple hostnames
69const db = await pg.connect("postgresql://postgres:5432/mydb");
70const cache = await redis.connect("redis://redis:6379");
71```
72
73## Persistent Storage
74
75Use Dockerfile VOLUME directives for persistent data:
76
77```dockerfile
78# postgres.Dockerfile
79FROM postgres:18
80VOLUME ["/var/lib/postgresql/data"]
81ENV POSTGRES_DB=myapp
82ENV POSTGRES_USER=app
83ENV POSTGRES_PASSWORD=secret
84```
85
86Build and push to Gordon:
87```bash
88docker build -f postgres.Dockerfile -t my-postgres:latest .
89docker push registry.mydomain.com/my-postgres:latest
90```
91
92Use in attachments:
93```toml
94[attachments]
95"app.mydomain.com" = ["my-postgres:latest"]
96```
97
98## Shared Attachments with Network Groups
99
100Share services between multiple apps using network groups:
101
102```toml
103[network_groups]
104"backend" = ["app.mydomain.com", "api.mydomain.com"]
105
106[attachments]
107"backend" = ["shared-postgres:latest", "shared-redis:latest"]
108```
109
110Both `app.mydomain.com` and `api.mydomain.com` can access the shared services.
111
112## Per-App vs Shared Attachments
113
114### Per-App Attachments
115
116Each app gets its own isolated service instances:
117
118```toml
119[attachments]
120"app.mydomain.com" = ["postgres:latest"]   # App's own postgres
121"api.mydomain.com" = ["postgres:latest"]   # API's own postgres
122```
123
124### Shared Attachments
125
126Multiple apps share the same service instances:
127
128```toml
129[network_groups]
130"backend" = ["app.mydomain.com", "api.mydomain.com"]
131
132[attachments]
133"backend" = ["postgres:latest", "redis:latest"]  # Shared by both
134```
135
136### Mixed Approach
137
138```toml
139[network_groups]
140"backend" = ["app.mydomain.com", "api.mydomain.com"]
141
142[attachments]
143"backend" = ["redis:latest"]                   # Shared cache
144"app.mydomain.com" = ["postgres:latest"]       # App's own database
145"api.mydomain.com" = ["postgres:latest"]       # API's own database
146```
147
148## Examples
149
150### Web App with Database
151
152```toml
153[routes]
154"app.mydomain.com" = "myapp:latest"
155
156[attachments]
157"app.mydomain.com" = ["postgres:18", "redis:7-alpine"]
158```
159
160### Microservices with Shared Queue
161
162```toml
163[routes]
164"orders.mydomain.com" = "orders-service:latest"
165"inventory.mydomain.com" = "inventory-service:latest"
166"notifications.mydomain.com" = "notifications-service:latest"
167
168[network_groups]
169"services" = ["orders.mydomain.com", "inventory.mydomain.com", "notifications.mydomain.com"]
170
171[attachments]
172"services" = ["rabbitmq:3-management"]
173"orders.mydomain.com" = ["orders-db:latest"]
174"inventory.mydomain.com" = ["inventory-db:latest"]
175```
176
177### Custom Service Images
178
179Build custom service images with your configuration:
180
181```dockerfile
182# my-postgres.Dockerfile
183FROM postgres:18
184VOLUME ["/var/lib/postgresql/data"]
185ENV POSTGRES_DB=production
186ENV POSTGRES_USER=appuser
187# Password should come from env file
188```
189
190```dockerfile
191# my-redis.Dockerfile
192FROM redis:7-alpine
193VOLUME ["/data"]
194CMD ["redis-server", "--appendonly", "yes"]
195```
196
197```toml
198[attachments]
199"app.mydomain.com" = ["my-postgres:latest", "my-redis:latest"]
200```
201
202## Container Naming
203
204Attachment containers are named:
205- `gordon-<domain>-<image-name>`
206- Example: `gordon-app-mydomain-com-postgres`
207
208## Labels
209
210Gordon adds labels to attachment containers:
211
212| Label | Value |
213|-------|-------|
214| `gordon.managed` | `true` |
215| `gordon.attachment` | `true` |
216| `gordon.attached-to` | Domain or group name |
217
218## CLI Management
219
220Manage attachments via the CLI without editing configuration files.
221
222### List Attachments
223
224```bash
225# List all attachments
226gordon attachments list
227
228# List attachments for a specific domain or network group
229gordon attachments list app.mydomain.com
230gordon attachments list backend
231
232# Remote mode
233gordon attachments list --remote https://gordon.mydomain.com --token $TOKEN
234```
235
236### Add Attachments
237
238```bash
239# Add attachment to a domain
240gordon attachments add app.mydomain.com postgres:18
241
242# Add attachment to a network group
243gordon attachments add backend redis:7-alpine
244
245# Remote mode
246gordon attachments add app.mydomain.com postgres:18 --remote https://gordon.mydomain.com --token $TOKEN
247```
248
249### Remove Attachments
250
251```bash
252# Remove attachment from a domain
253gordon attachments remove app.mydomain.com postgres:18
254
255# Remove from network group
256gordon attachments remove backend redis:7-alpine
257
258# Remote mode
259gordon attachments remove app.mydomain.com postgres:18 --remote https://gordon.mydomain.com --token $TOKEN
260```
261
262### Alias
263
264The `gordon attach` command is an alias for `gordon attachments`:
265
266```bash
267gordon attach list
268gordon attach add app.mydomain.com postgres:18
269gordon attach remove app.mydomain.com postgres:18
270```
271
272## Related
273
274- [Network Isolation](./network-isolation.md)
275- [Network Groups](./network-groups.md)
276- [Routes](./routes.md)
277- [CLI Commands](/docs/cli/index.md)

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