v2.2.5 511b814

attachments.md RawHistoryFolder

Attachments Configuration

Attach service dependencies (databases, caches, queues) to your applications.

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:15
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:15", "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:15
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
source
1# Attachments Configuration
2
3Attach service dependencies (databases, caches, queues) to your applications.
4
5## Configuration
6
7```toml
8[attachments]
9"app.mydomain.com" = ["postgres:latest", "redis:latest"]
10"api.mydomain.com" = ["postgres:latest"]
11```
12
13## Syntax
14
15```toml
16[attachments]
17"<domain-or-group>" = ["<image>:<tag>", "<image>:<tag>"]
18```
19
20| Component | Description |
21|-----------|-------------|
22| `domain-or-group` | Route domain or network group name |
23| `image:tag` | Service images to deploy alongside the app |
24
25## How Attachments Work
26
27When you define attachments:
28
291. Gordon deploys attachment containers to the same network as your app
302. Services are accessible by their image name (before the colon)
313. Attachments start before your main application
324. Attachments persist across app updates
33
34```
35┌─────────────────────────────────────────────────────┐
36│ Network: gordon-app-mydomain-com                    │
37│                                                     │
38│  ┌──────────┐    ┌──────────┐    ┌──────────┐     │
39│  │   App    │───>│ postgres │    │  redis   │     │
40│  │  :3000   │    │  :5432   │    │  :6379   │     │
41│  └──────────┘    └──────────┘    └──────────┘     │
42│                                                     │
43└─────────────────────────────────────────────────────┘
44```
45
46## Service Discovery
47
48Attachments are accessible by their image name within the network:
49
50```toml
51[attachments]
52"app.mydomain.com" = ["postgres:latest", "redis:latest"]
53```
54
55In your application:
56```javascript
57// Connect using simple hostnames
58const db = await pg.connect("postgresql://postgres:5432/mydb");
59const cache = await redis.connect("redis://redis:6379");
60```
61
62## Persistent Storage
63
64Use Dockerfile VOLUME directives for persistent data:
65
66```dockerfile
67# postgres.Dockerfile
68FROM postgres:15
69VOLUME ["/var/lib/postgresql/data"]
70ENV POSTGRES_DB=myapp
71ENV POSTGRES_USER=app
72ENV POSTGRES_PASSWORD=secret
73```
74
75Build and push to Gordon:
76```bash
77docker build -f postgres.Dockerfile -t my-postgres:latest .
78docker push registry.mydomain.com/my-postgres:latest
79```
80
81Use in attachments:
82```toml
83[attachments]
84"app.mydomain.com" = ["my-postgres:latest"]
85```
86
87## Shared Attachments with Network Groups
88
89Share services between multiple apps using network groups:
90
91```toml
92[network_groups]
93"backend" = ["app.mydomain.com", "api.mydomain.com"]
94
95[attachments]
96"backend" = ["shared-postgres:latest", "shared-redis:latest"]
97```
98
99Both `app.mydomain.com` and `api.mydomain.com` can access the shared services.
100
101## Per-App vs Shared Attachments
102
103### Per-App Attachments
104
105Each app gets its own isolated service instances:
106
107```toml
108[attachments]
109"app.mydomain.com" = ["postgres:latest"]   # App's own postgres
110"api.mydomain.com" = ["postgres:latest"]   # API's own postgres
111```
112
113### Shared Attachments
114
115Multiple apps share the same service instances:
116
117```toml
118[network_groups]
119"backend" = ["app.mydomain.com", "api.mydomain.com"]
120
121[attachments]
122"backend" = ["postgres:latest", "redis:latest"]  # Shared by both
123```
124
125### Mixed Approach
126
127```toml
128[network_groups]
129"backend" = ["app.mydomain.com", "api.mydomain.com"]
130
131[attachments]
132"backend" = ["redis:latest"]                   # Shared cache
133"app.mydomain.com" = ["postgres:latest"]       # App's own database
134"api.mydomain.com" = ["postgres:latest"]       # API's own database
135```
136
137## Examples
138
139### Web App with Database
140
141```toml
142[routes]
143"app.mydomain.com" = "myapp:latest"
144
145[attachments]
146"app.mydomain.com" = ["postgres:15", "redis:7-alpine"]
147```
148
149### Microservices with Shared Queue
150
151```toml
152[routes]
153"orders.mydomain.com" = "orders-service:latest"
154"inventory.mydomain.com" = "inventory-service:latest"
155"notifications.mydomain.com" = "notifications-service:latest"
156
157[network_groups]
158"services" = ["orders.mydomain.com", "inventory.mydomain.com", "notifications.mydomain.com"]
159
160[attachments]
161"services" = ["rabbitmq:3-management"]
162"orders.mydomain.com" = ["orders-db:latest"]
163"inventory.mydomain.com" = ["inventory-db:latest"]
164```
165
166### Custom Service Images
167
168Build custom service images with your configuration:
169
170```dockerfile
171# my-postgres.Dockerfile
172FROM postgres:15
173VOLUME ["/var/lib/postgresql/data"]
174ENV POSTGRES_DB=production
175ENV POSTGRES_USER=appuser
176# Password should come from env file
177```
178
179```dockerfile
180# my-redis.Dockerfile
181FROM redis:7-alpine
182VOLUME ["/data"]
183CMD ["redis-server", "--appendonly", "yes"]
184```
185
186```toml
187[attachments]
188"app.mydomain.com" = ["my-postgres:latest", "my-redis:latest"]
189```
190
191## Container Naming
192
193Attachment containers are named:
194- `gordon-<domain>-<image-name>`
195- Example: `gordon-app-mydomain-com-postgres`
196
197## Labels
198
199Gordon adds labels to attachment containers:
200
201| Label | Value |
202|-------|-------|
203| `gordon.managed` | `true` |
204| `gordon.attachment` | `true` |
205| `gordon.attached-to` | Domain or group name |
206
207## Related
208
209- [Network Isolation](./network-isolation.md)
210- [Network Groups](./network-groups.md)
211- [Routes](./routes.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