Skip to content

Commit a88380f

Browse files
authored
Merge pull request #292 from meith-dev/claude/coolify-compose-deploy
2 parents 43151c0 + 5e5f978 commit a88380f

5 files changed

Lines changed: 61 additions & 1 deletion

File tree

apps/web/public/create-board.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,10 @@ services:
489489
# always applied before the first request rather than racing it.
490490
migrate:
491491
image: ${MEITH_IMAGE:?set this to the image the build workflow's Summary just printed, e.g. ghcr.io/<you>/__MEITH_BOARD_NAME__:latest}
492+
# Pull the tag on every deploy. Compose keeps an image it already has, so
493+
# a rebuilt `:latest` is otherwise never fetched and a redeploy quietly
494+
# runs the old code.
495+
pull_policy: always
492496
environment:
493497
COMMUNITY_ROLE: migrate
494498
DATABASE_URL: postgres://community:$SERVICE_PASSWORD_POSTGRES@postgres:5432/community
@@ -501,9 +505,21 @@ services:
501505
502506
web:
503507
image: ${MEITH_IMAGE:?set this to the image the build workflow's Summary just printed, e.g. ghcr.io/<you>/__MEITH_BOARD_NAME__:latest}
508+
pull_policy: always
504509
restart: unless-stopped
505510
mem_limit: ${WEB_MEM_LIMIT:-1g}
506511
cpus: ${WEB_CPUS:-2}
512+
# A readiness probe Coolify gates a rolling deploy on: with "Rolling
513+
# update" enabled on the resource, the new container must answer
514+
# /api/ready before the old one is retired, so a redeploy swaps in with no
515+
# gap. Without it Coolify recreates the stack — old removed, then new
516+
# started — and the board is down while the new web boots.
517+
healthcheck:
518+
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/ready').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
519+
interval: 30s
520+
timeout: 5s
521+
start_period: 20s
522+
retries: 3
507523
environment:
508524
# Ask Coolify for a domain on port 3000, then hand the board the same
509525
# thing with a scheme in front.

docs/getting-started/deployment/coolify.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,18 @@ ceilings, which default to a small VPS: `WEB_MEM_LIMIT`, `WEB_CPUS`,
228228
`MEITH_IMAGE` does, so a larger server is a variable on the resource,
229229
never an edit to the file.
230230

231+
> [!NOTE]
232+
> **Redeploys, and deploying without a gap.** The `web` and `migrate`
233+
> services set `pull_policy: always`, so every **Redeploy** fetches the
234+
> current image for the tag rather than reusing a `:latest` the host already
235+
> has — a rebuild on `main` is picked up without pinning a new digest.
236+
> Coolify recreates a compose stack by default, though: it stops the old
237+
> containers before starting the new ones, so the board is briefly down while
238+
> `web` boots. To close that gap, turn on **Rolling update** for the resource
239+
> (its **General** settings). `web` declares a `/api/ready` healthcheck for
240+
> exactly this — with rolling enabled Coolify waits for the new container to
241+
> pass it before retiring the old one, so the swap carries no downtime.
242+
231243
## 4. Run the installer
232244

233245
Open `https://your-domain/install`.

packages/create-meith/src/scaffold.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ const SELF_HOST_TREE_DIGESTS: Readonly<Record<string, string>> = {
583583
'.dockerignore': '620ca0bdf50f76e3817c135ee43afe56669b7b3caaad86b4926021cc52dd3c4b',
584584
'.github/dependabot.yml': '6cae93a9aa7b08a6f62e94db7c940d74b3657ff81454e6dc6b6e485b1afa3ac8',
585585
'.github/workflows/build.yml': 'd61b4b1fe933e5fd4d584e63ec45a999e47145529c0c89f9bb9eef0580fb4915',
586-
'docker-compose.yaml': 'bb57c62317b1db6572a553f578bc2f603fa63cc69bc45d4ffdf938e044f19960',
586+
'docker-compose.yaml': '42bc81028ffce83ae55ac407ece0607b144f73674822610064da3eabe77fc6d1',
587587
'README.md': 'c2af780df2ca761943117adbe44c4ce464bb6781654dcb9928b78d21066221d4',
588588
}
589589

packages/create-meith/src/scaffold.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,10 @@ services:
823823
# always applied before the first request rather than racing it.
824824
migrate:
825825
image: \${MEITH_IMAGE:?set this to the image the build workflow's Summary just printed, e.g. ghcr.io/<you>/${name}:latest}
826+
# Pull the tag on every deploy. Compose keeps an image it already has, so
827+
# a rebuilt \`:latest\` is otherwise never fetched and a redeploy quietly
828+
# runs the old code.
829+
pull_policy: always
826830
environment:
827831
COMMUNITY_ROLE: migrate
828832
DATABASE_URL: postgres://community:$SERVICE_PASSWORD_POSTGRES@postgres:5432/community
@@ -835,9 +839,21 @@ services:
835839
836840
web:
837841
image: \${MEITH_IMAGE:?set this to the image the build workflow's Summary just printed, e.g. ghcr.io/<you>/${name}:latest}
842+
pull_policy: always
838843
restart: unless-stopped
839844
mem_limit: \${WEB_MEM_LIMIT:-1g}
840845
cpus: \${WEB_CPUS:-2}
846+
# A readiness probe Coolify gates a rolling deploy on: with "Rolling
847+
# update" enabled on the resource, the new container must answer
848+
# /api/ready before the old one is retired, so a redeploy swaps in with no
849+
# gap. Without it Coolify recreates the stack — old removed, then new
850+
# started — and the board is down while the new web boots.
851+
healthcheck:
852+
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/ready').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
853+
interval: 30s
854+
timeout: 5s
855+
start_period: 20s
856+
retries: 3
841857
environment:
842858
# Ask Coolify for a domain on port 3000, then hand the board the same
843859
# thing with a scheme in front.

templates/self-host/docker-compose.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ services:
3232
# always applied before the first request rather than racing it.
3333
migrate:
3434
image: ${MEITH_IMAGE:?set this to the image the build workflow's Summary just printed, e.g. ghcr.io/<you>/meith-board:latest}
35+
# Pull the tag on every deploy. Compose keeps an image it already has, so
36+
# a rebuilt `:latest` is otherwise never fetched and a redeploy quietly
37+
# runs the old code.
38+
pull_policy: always
3539
environment:
3640
COMMUNITY_ROLE: migrate
3741
DATABASE_URL: postgres://community:$SERVICE_PASSWORD_POSTGRES@postgres:5432/community
@@ -44,9 +48,21 @@ services:
4448

4549
web:
4650
image: ${MEITH_IMAGE:?set this to the image the build workflow's Summary just printed, e.g. ghcr.io/<you>/meith-board:latest}
51+
pull_policy: always
4752
restart: unless-stopped
4853
mem_limit: ${WEB_MEM_LIMIT:-1g}
4954
cpus: ${WEB_CPUS:-2}
55+
# A readiness probe Coolify gates a rolling deploy on: with "Rolling
56+
# update" enabled on the resource, the new container must answer
57+
# /api/ready before the old one is retired, so a redeploy swaps in with no
58+
# gap. Without it Coolify recreates the stack — old removed, then new
59+
# started — and the board is down while the new web boots.
60+
healthcheck:
61+
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/ready').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
62+
interval: 30s
63+
timeout: 5s
64+
start_period: 20s
65+
retries: 3
5066
environment:
5167
# Ask Coolify for a domain on port 3000, then hand the board the same
5268
# thing with a scheme in front.

0 commit comments

Comments
 (0)