dokku-compose 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 guess
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,555 @@
1
+ # dokku-compose
2
+
3
+ - 📄 **Declarative** -- Define your entire Dokku server in a single YAML file
4
+ - 🔁 **Idempotent** -- Run it twice, nothing changes. Safe to re-run anytime
5
+ - 👀 **Dry-run** -- Preview every command before it touches your server
6
+ - 🔍 **Diff** -- See exactly what's out of sync before applying changes
7
+ - 🏗️ **Modular** -- One file per Dokku namespace. Easy to read, extend, and debug
8
+
9
+ [![Tests](https://github.com/guess/dokku-compose/actions/workflows/tests.yml/badge.svg)](https://github.com/guess/dokku-compose/actions/workflows/tests.yml)
10
+ [![License: MIT](https://img.shields.io/github/license/guess/dokku-compose)](LICENSE)
11
+ [![Latest Release](https://img.shields.io/github/v/release/guess/dokku-compose)](https://github.com/guess/dokku-compose/releases/latest)
12
+
13
+ <p align="center">
14
+ <img src="assets/dokku-compose.png" alt="dokku-compose" width="300">
15
+ </p>
16
+
17
+ ## Why
18
+
19
+ Configuring a Dokku server means running dozens of imperative commands in the right order. Miss one and your deploy breaks. Change servers and you're starting over.
20
+
21
+ `dokku-compose` replaces that with a single YAML file. Like Docker Compose, but for Dokku.
22
+
23
+ ## A Complete Example
24
+
25
+ ```yaml
26
+ dokku:
27
+ version: "0.35.12"
28
+
29
+ plugins:
30
+ postgres:
31
+ url: https://github.com/dokku/dokku-postgres.git
32
+ version: "1.41.0"
33
+ redis:
34
+ url: https://github.com/dokku/dokku-redis.git
35
+
36
+ services:
37
+ api-postgres:
38
+ plugin: postgres
39
+ version: "17-3.5"
40
+ shared-cache:
41
+ plugin: redis
42
+
43
+ networks:
44
+ - backend-net
45
+
46
+ domains:
47
+ - example.com
48
+
49
+ nginx:
50
+ client-max-body-size: "50m"
51
+
52
+ logs:
53
+ max-size: "50m"
54
+
55
+ apps:
56
+ api:
57
+ build:
58
+ context: apps/api
59
+ dockerfile: docker/prod/api/Dockerfile
60
+ env:
61
+ APP_ENV: production
62
+ APP_SECRET: "${SECRET_KEY}"
63
+ domains:
64
+ - api.example.com
65
+ links:
66
+ - api-postgres
67
+ - shared-cache
68
+ networks:
69
+ - backend-net
70
+ ports:
71
+ - "https:4001:4000"
72
+ ssl:
73
+ certfile: certs/example.com/fullchain.pem
74
+ keyfile: certs/example.com/privkey.pem
75
+ storage:
76
+ - "/var/lib/dokku/data/storage/api/uploads:/app/uploads"
77
+ nginx:
78
+ client-max-body-size: "15m"
79
+ proxy-read-timeout: "120s"
80
+ checks:
81
+ wait-to-retire: 60
82
+ disabled:
83
+ - worker
84
+
85
+ worker:
86
+ links:
87
+ - api-postgres
88
+ - shared-cache
89
+ checks: false
90
+ proxy:
91
+ enabled: false
92
+ ```
93
+
94
+ ## Quick Start
95
+
96
+ ```bash
97
+ # Install
98
+ npm install -g dokku-compose
99
+
100
+ # Create a starter config
101
+ dokku-compose init myapp
102
+
103
+ # Preview what will happen
104
+ dokku-compose up --dry-run
105
+
106
+ # Apply configuration
107
+ dokku-compose up
108
+
109
+ # Or apply to a remote server over SSH
110
+ DOKKU_HOST=my-server.example.com dokku-compose up
111
+ ```
112
+
113
+ Requires Node.js >= 18. See the [Installation Reference →](docs/reference/install.md) for requirements and remote execution details.
114
+
115
+ ## Features
116
+
117
+ Features are listed roughly in execution order — the sequence `dokku-compose up` follows.
118
+
119
+ ### Dokku Version Check
120
+
121
+ Declare the expected Dokku version. A warning is logged if the running version doesn't match.
122
+
123
+ ```yaml
124
+ dokku:
125
+ version: "0.35.12"
126
+ ```
127
+
128
+ ```
129
+ [dokku ] WARN: Version mismatch: running 0.34.0, config expects 0.35.12
130
+ ```
131
+
132
+ Dokku must be pre-installed on the target server.
133
+
134
+ ### Application Management
135
+
136
+ Create and destroy Dokku apps idempotently. If the app already exists, it's skipped.
137
+
138
+ ```yaml
139
+ apps:
140
+ api:
141
+ # per-app configuration goes here
142
+ ```
143
+
144
+ [Application Management Reference →](docs/reference/apps.md)
145
+
146
+ ### Environment Variables
147
+
148
+ Set config vars per app or globally. All declared vars are converged — orphaned vars are automatically unset.
149
+
150
+ ```yaml
151
+ apps:
152
+ api:
153
+ env:
154
+ APP_ENV: production
155
+ APP_SECRET: "${SECRET_KEY}"
156
+ ```
157
+
158
+ [Environment Variables Reference →](docs/reference/config.md)
159
+
160
+ ### Build
161
+
162
+ Configure Dockerfile builds: build context, Dockerfile path, app.json location, and build args. Key names follow docker-compose conventions.
163
+
164
+ ```yaml
165
+ apps:
166
+ api:
167
+ build:
168
+ context: apps/api
169
+ dockerfile: docker/prod/api/Dockerfile
170
+ app_json: docker/prod/api/app.json
171
+ args:
172
+ SENTRY_AUTH_TOKEN: "${SENTRY_AUTH_TOKEN}"
173
+ ```
174
+
175
+ [Build Reference →](docs/reference/builder.md)
176
+
177
+ ### Docker Options
178
+
179
+ Add custom Docker options per build phase (`build`, `deploy`, `run`). Each phase is cleared and re-populated on every `up` for idempotent convergence.
180
+
181
+ ```yaml
182
+ apps:
183
+ api:
184
+ docker_options:
185
+ deploy:
186
+ - "--shm-size 256m"
187
+ run:
188
+ - "--ulimit nofile=12"
189
+ ```
190
+
191
+ [Docker Options Reference →](docs/reference/docker_options.md)
192
+
193
+ ### Networks
194
+
195
+ Create shared Docker networks and configure per-app network properties.
196
+
197
+ ```yaml
198
+ networks:
199
+ - backend-net
200
+
201
+ apps:
202
+ api:
203
+ networks: # attach-post-deploy
204
+ - backend-net
205
+ network: # other network:set properties
206
+ attach_post_create:
207
+ - init-net
208
+ initial_network: custom-bridge
209
+ bind_all_interfaces: true
210
+ tld: internal
211
+ ```
212
+
213
+ `down --force` clears network settings and destroys declared networks.
214
+
215
+ [Networks Reference →](docs/reference/network.md)
216
+
217
+ ### Domains
218
+
219
+ Configure custom domains per app or globally.
220
+
221
+ ```yaml
222
+ domains:
223
+ - example.com
224
+
225
+ apps:
226
+ api:
227
+ domains:
228
+ - api.example.com
229
+ - api.example.co
230
+ ```
231
+
232
+ [Domains Reference →](docs/reference/domains.md)
233
+
234
+ ### Port Mappings
235
+
236
+ Map external ports to container ports using `SCHEME:HOST_PORT:CONTAINER_PORT` format.
237
+
238
+ ```yaml
239
+ apps:
240
+ api:
241
+ ports:
242
+ - "https:4001:4000"
243
+ ```
244
+
245
+ Comparison is order-insensitive. `down --force` clears port mappings before destroying the app.
246
+
247
+ [Port Mappings Reference →](docs/reference/ports.md)
248
+
249
+ ### SSL Certificates
250
+
251
+ Specify cert and key file paths. Idempotent — skips if SSL is already enabled. Set to `false` to remove an existing certificate.
252
+
253
+ ```yaml
254
+ apps:
255
+ api:
256
+ ssl: # add cert (idempotent)
257
+ certfile: certs/example.com/fullchain.pem
258
+ keyfile: certs/example.com/privkey.pem
259
+ worker:
260
+ ssl: false # remove cert
261
+ ```
262
+
263
+ [SSL Certificates Reference →](docs/reference/certs.md)
264
+
265
+ ### Proxy
266
+
267
+ Enable or disable the proxy for an app, and optionally select the proxy implementation (nginx, caddy, haproxy, traefik). All operations are idempotent.
268
+
269
+ ```yaml
270
+ apps:
271
+ api:
272
+ proxy: true # shorthand enable
273
+
274
+ worker:
275
+ proxy: false # shorthand disable
276
+
277
+ caddy-app:
278
+ proxy:
279
+ enabled: true
280
+ type: caddy # proxy:set caddy-app caddy
281
+ ```
282
+
283
+ [Proxy Reference →](docs/reference/proxy.md)
284
+
285
+ ### Persistent Storage
286
+
287
+ Declare persistent bind mounts for an app. Mounts are fully converged on each `up` run — new mounts are added, mounts removed from YAML are unmounted, and existing mounts are skipped.
288
+
289
+ ```yaml
290
+ apps:
291
+ api:
292
+ storage:
293
+ - "/var/lib/dokku/data/storage/api/uploads:/app/uploads"
294
+ ```
295
+
296
+ Host directories must exist before mounting. On `down`, declared mounts are unmounted.
297
+
298
+ [Storage Reference →](docs/reference/storage.md)
299
+
300
+ ### Nginx Configuration
301
+
302
+ Set any nginx property supported by Dokku via a key-value map — per-app or globally.
303
+
304
+ ```yaml
305
+ nginx: # global defaults
306
+ client-max-body-size: "50m"
307
+
308
+ apps:
309
+ api:
310
+ nginx: # per-app overrides
311
+ client-max-body-size: "15m"
312
+ proxy-read-timeout: "120s"
313
+ ```
314
+
315
+ [Nginx Reference →](docs/reference/nginx.md)
316
+
317
+ ### Zero-Downtime Checks
318
+
319
+ Configure zero-downtime deploy check properties, disable checks entirely, or control per process type. Properties are idempotent — current values are checked before setting.
320
+
321
+ ```yaml
322
+ apps:
323
+ api:
324
+ checks:
325
+ wait-to-retire: 60
326
+ attempts: 5
327
+ disabled:
328
+ - worker
329
+ skipped:
330
+ - cron
331
+ worker:
332
+ checks: false # disable all checks (causes downtime)
333
+ ```
334
+
335
+ [Zero-Downtime Checks Reference →](docs/reference/checks.md)
336
+
337
+ ### Log Management
338
+
339
+ Configure log retention and shipping globally or per-app.
340
+
341
+ ```yaml
342
+ logs: # global defaults
343
+ max-size: "50m"
344
+ vector-sink: "console://?encoding[codec]=json"
345
+
346
+ apps:
347
+ api:
348
+ logs: # per-app overrides
349
+ max-size: "10m"
350
+ ```
351
+
352
+ [Log Management Reference →](docs/reference/logs.md)
353
+
354
+ ### Plugins and Services
355
+
356
+ Install plugins and declare service instances. Services are created before apps during `up` and linked on demand.
357
+
358
+ ```yaml
359
+ plugins:
360
+ postgres:
361
+ url: https://github.com/dokku/dokku-postgres.git
362
+ version: "1.41.0"
363
+
364
+ services:
365
+ api-postgres:
366
+ plugin: postgres
367
+
368
+ apps:
369
+ api:
370
+ links:
371
+ - api-postgres
372
+ ```
373
+
374
+ [Plugins and Services Reference →](docs/reference/plugins.md)
375
+
376
+ ## Commands
377
+
378
+ | Command | Description |
379
+ |---------|-------------|
380
+ | `dokku-compose init [app...]` | Create a starter `dokku-compose.yml` |
381
+ | `dokku-compose up` | Create/update apps and services to match config |
382
+ | `dokku-compose down --force` | Destroy apps and services (requires `--force`) |
383
+ | `dokku-compose ps` | Show status of configured apps |
384
+ | `dokku-compose validate` | Validate config file offline (no server contact) |
385
+ | `dokku-compose export` | Reverse-engineer server state into YAML |
386
+ | `dokku-compose diff` | Show what's out of sync between config and server |
387
+
388
+ ### `ps` — Show Status
389
+
390
+ Queries each configured app and prints its deploy status:
391
+
392
+ ```
393
+ $ dokku-compose ps
394
+ api running
395
+ worker running
396
+ web not created
397
+ ```
398
+
399
+ ### `down` — Tear Down
400
+
401
+ Destroys apps and their linked services. Requires `--force` as a safety measure. For each app, services are unlinked first, then the app is destroyed. Service instances from the top-level `services:` section are destroyed after all apps.
402
+
403
+ ```bash
404
+ dokku-compose down --force myapp # Destroy one app and its services
405
+ dokku-compose down --force # Destroy all configured apps
406
+ ```
407
+
408
+ ## Options
409
+
410
+ | Option | Description |
411
+ |--------|-------------|
412
+ | `--file <path>` | Config file (default: `dokku-compose.yml`) |
413
+ | `--dry-run` | Print commands without executing |
414
+ | `--fail-fast` | Stop on first error (default: continue to next app) |
415
+ | `--remove-orphans` | Destroy services and networks not in config |
416
+ | `--help` | Show usage |
417
+ | `--version` | Show version |
418
+
419
+ ## Examples
420
+
421
+ ```bash
422
+ dokku-compose up # Configure all apps
423
+ dokku-compose up myapp # Configure one app
424
+ dokku-compose up --dry-run # Preview changes
425
+ dokku-compose down --force myapp # Destroy an app
426
+ dokku-compose ps # Show status
427
+ ```
428
+
429
+ ## Execution Modes
430
+
431
+ ```bash
432
+ # Run locally on the Dokku server
433
+ dokku-compose up
434
+
435
+ # Run remotely over SSH
436
+ DOKKU_HOST=my-server.example.com dokku-compose up
437
+ ```
438
+
439
+ When `DOKKU_HOST` is set, all Dokku commands are sent over SSH. This is the typical workflow — keep `dokku-compose.yml` in your project repo and apply it from your local machine. SSH key access to the Dokku server is required.
440
+
441
+ ## What `up` Does
442
+
443
+ Idempotently ensures desired state, in order:
444
+
445
+ 1. Check Dokku version (warn on mismatch)
446
+ 2. Install missing plugins
447
+ 3. Set global config (domains, env vars, nginx defaults)
448
+ 4. Create shared networks
449
+ 5. Create service instances (from top-level `services:`)
450
+ 6. For each app:
451
+ - Create app (if not exists)
452
+ - Set domains, link/unlink services, attach networks
453
+ - Enable/disable proxy, set ports, add SSL, mount storage
454
+ - Configure nginx, checks, logs, env vars, build, and docker options
455
+
456
+ Running `up` twice produces no changes — every step checks current state before acting.
457
+
458
+ `up` is mostly additive. Removing a key (e.g. deleting a `ports:` block) won't remove the corresponding setting from Dokku. The exception is `links:`, which is fully declarative — services not in the list are unlinked. Use `down --force` to fully reset an app, or `--remove-orphans` to destroy services and networks no longer in config.
459
+
460
+ ## Output
461
+
462
+ ```
463
+ [networks ] Creating backend-net... done
464
+ [services ] Creating api-postgres (postgres 17-3.5)... done
465
+ [services ] Creating api-redis (redis)... done
466
+ [services ] Creating shared-cache (redis)... done
467
+ [api ] Creating app... done
468
+ [api ] Setting domains: api.example.com... done
469
+ [api ] Linking api-postgres... done
470
+ [api ] Linking api-redis... done
471
+ [api ] Linking shared-cache... done
472
+ [api ] Setting ports https:4001:4000... done
473
+ [api ] Adding SSL certificate... done
474
+ [api ] Mounting /var/lib/dokku/data/storage/api/uploads:/app/uploads... done
475
+ [api ] Setting nginx client-max-body-size=15m... done
476
+ [api ] Setting checks wait-to-retire=60... done
477
+ [api ] Setting 2 env var(s)... done
478
+ [worker ] Creating app... already configured
479
+ [worker ] Linking shared-cache... already configured
480
+ ```
481
+
482
+ ## Architecture
483
+
484
+ <details>
485
+ <summary>File structure</summary>
486
+
487
+ ```
488
+ dokku-compose/
489
+ ├── bin/
490
+ │ └── dokku-compose # Entry point (delegates to src/index.ts via tsx)
491
+ ├── src/
492
+ │ ├── index.ts # CLI entry point (Commander.js)
493
+ │ ├── core/
494
+ │ │ ├── schema.ts # Zod config schema and types
495
+ │ │ ├── config.ts # YAML loading and parsing
496
+ │ │ ├── dokku.ts # Runner interface and factory
497
+ │ │ └── logger.ts # Colored output helpers
498
+ │ ├── modules/ # One file per Dokku namespace
499
+ │ │ ├── apps.ts # dokku apps:*
500
+ │ │ ├── builder.ts # dokku builder:*, builder-dockerfile:*, app-json:*
501
+ │ │ ├── certs.ts # dokku certs:*
502
+ │ │ ├── checks.ts # dokku checks:*
503
+ │ │ ├── config.ts # dokku config:*
504
+ │ │ ├── docker_options.ts # dokku docker-options:*
505
+ │ │ ├── domains.ts # dokku domains:*
506
+ │ │ ├── logs.ts # dokku logs:*
507
+ │ │ ├── network.ts # dokku network:*
508
+ │ │ ├── nginx.ts # dokku nginx:*
509
+ │ │ ├── plugins.ts # dokku plugin:*
510
+ │ │ ├── ports.ts # dokku ports:*
511
+ │ │ ├── proxy.ts # dokku proxy:*
512
+ │ │ ├── registry.ts # dokku registry:*
513
+ │ │ ├── scheduler.ts # dokku scheduler:*
514
+ │ │ ├── services.ts # Service instances, links, plugin scripts
515
+ │ │ └── storage.ts # dokku storage:*
516
+ │ ├── commands/
517
+ │ │ ├── up.ts # up command orchestration
518
+ │ │ ├── down.ts # down command orchestration
519
+ │ │ ├── export.ts # export command
520
+ │ │ ├── diff.ts # diff command
521
+ │ │ └── validate.ts # validate command (offline)
522
+ │ └── tests/
523
+ │ ├── fixtures/ # Test YAML configs
524
+ │ └── *.test.ts # Unit tests per module
525
+ └── dokku-compose.yml.example
526
+ ```
527
+
528
+ Each `src/modules/*.ts` file maps to one Dokku command namespace and exports `ensure*()`, `destroy*()`, and `export*()` functions. See [CLAUDE.md](CLAUDE.md) for development conventions.
529
+
530
+ </details>
531
+
532
+ ## Development
533
+
534
+ ```bash
535
+ git clone https://github.com/guess/dokku-compose.git
536
+ cd dokku-compose
537
+ bun install
538
+
539
+ # Run all tests
540
+ bun test
541
+
542
+ # Run a specific module's tests
543
+ bun test src/modules/services.test.ts
544
+ ```
545
+
546
+ Tests use [Bun's test runner](https://bun.sh/docs/cli/test) with a mocked `Runner` — no real Dokku server needed.
547
+
548
+ ```bash
549
+ # Cut a release (bumps version, tags, pushes — CI publishes to npm)
550
+ scripts/release.sh 0.3.0
551
+ ```
552
+
553
+ ## License
554
+
555
+ [MIT](LICENSE)
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+ # Delegate to TypeScript implementation
3
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
4
+ exec bun "${SCRIPT_DIR}/../src/index.ts" "$@"
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node