create-meith 0.34.0 → 0.35.1

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/dist/bin.mjs CHANGED
@@ -349,6 +349,7 @@ var SELF_HOST_DEPLOY_KIT = [
349
349
  "Dockerfile.prebuilt",
350
350
  "docker-compose.yaml",
351
351
  "docker-compose.prebuilt.yaml",
352
+ "docker-compose.byhand.yaml",
352
353
  "docker-entrypoint.sh",
353
354
  "docker-healthcheck.sh"
354
355
  ];
@@ -1292,6 +1293,201 @@ services:
1292
1293
  depends_on:
1293
1294
  - web
1294
1295
 
1296
+ volumes:
1297
+ pgdata:
1298
+ uploads:
1299
+ backups:
1300
+ `
1301
+ );
1302
+ files.set(
1303
+ "docker-compose.byhand.yaml",
1304
+ `# ${name}, deployed by Docker Compose without a panel \u2014 your own \`.env\`,
1305
+ # your own reverse proxy, no Coolify. The same four services as
1306
+ # docker-compose.yaml and docker-compose.prebuilt.yaml above it (db,
1307
+ # migrate, web, worker), reshaped for a machine with nothing generating
1308
+ # secrets for you: every value Coolify would have filled in \u2014 the database
1309
+ # password, AUTH_SECRET, TICK_SECRET, the board's own address \u2014 comes from
1310
+ # a \`.env\` beside this file instead. See the meith repository's
1311
+ # docs/getting-started/deployment/docker-compose.md, which this file is
1312
+ # the last step of.
1313
+ #
1314
+ # \`docker compose\` only auto-discovers a file literally named
1315
+ # docker-compose.yaml, and that name is already spoken for \u2014 Coolify's own
1316
+ # zero-configuration default. Put \`COMPOSE_FILE=docker-compose.byhand.yaml\`
1317
+ # in \`.env\` once, and every plain \`docker compose ...\` command reads this
1318
+ # file without a \`-f\`, including the ones the rest of this project's
1319
+ # documentation already shows you.
1320
+ #
1321
+ # \`docker compose up -d --build\` builds \`Dockerfile\` from this repository,
1322
+ # on this machine \u2014 the same trade the quick-start path above takes. For a
1323
+ # low-spec server, build \`Dockerfile.prebuilt\` somewhere else instead (push
1324
+ # to GitHub and let \`.github/workflows/build.yml\` do it, or run
1325
+ # \`docker build -f Dockerfile.prebuilt ...\` by hand) and change the two
1326
+ # \`build: .\` lines below to \`image: <that image>:<version>\` \u2014 the
1327
+ # substitution docs/getting-started/deployment/docker-compose.md, "Building
1328
+ # somewhere else", walks through.
1329
+ services:
1330
+ postgres:
1331
+ image: postgres:18-alpine@sha256:d3e1620b530c944afa6e887d22eb899824da68e19c52024bf98f5220c88a65b2
1332
+ restart: unless-stopped
1333
+ mem_limit: \${POSTGRES_MEM_LIMIT:-1g}
1334
+ cpus: \${POSTGRES_CPUS:-1}
1335
+ logging:
1336
+ driver: json-file
1337
+ options:
1338
+ max-size: 10m
1339
+ max-file: '3'
1340
+ environment:
1341
+ POSTGRES_USER: community
1342
+ # Generate with \`openssl rand -hex 32\`, not base64 \u2014 this value is
1343
+ # substituted into the postgres:// URL below, and base64's alphabet
1344
+ # includes \`/\` and \`+\`, which make the connection string unparseable.
1345
+ POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-community}
1346
+ POSTGRES_DB: community
1347
+ volumes:
1348
+ - pgdata:/var/lib/postgresql
1349
+ healthcheck:
1350
+ test: ['CMD-SHELL', 'pg_isready -U community -d community']
1351
+ interval: 10s
1352
+ timeout: 5s
1353
+ retries: 5
1354
+
1355
+ # Runs to completion, then exits. web waits for it, so the schema is
1356
+ # always applied before the first request rather than racing it.
1357
+ migrate:
1358
+ build: .
1359
+ image: ${name}
1360
+ environment:
1361
+ MEITH_ROLE: migrate
1362
+ DATABASE_URL: postgres://community:\${POSTGRES_PASSWORD:-community}@postgres:5432/community
1363
+ AUTH_SECRET: \${AUTH_SECRET:?AUTH_SECRET must be set}
1364
+ TICK_SECRET: \${TICK_SECRET:?TICK_SECRET must be set}
1365
+ FILESTORE_DRIVER: local
1366
+ # So that "back up before migrating" (a board setting) can ship the
1367
+ # bundle it takes to the same off-site destination web uses.
1368
+ BACKUP_S3_BUCKET: \${BACKUP_S3_BUCKET:-}
1369
+ BACKUP_S3_REGION: \${BACKUP_S3_REGION:-}
1370
+ BACKUP_S3_ACCESS_KEY_ID: \${BACKUP_S3_ACCESS_KEY_ID:-}
1371
+ BACKUP_S3_SECRET_ACCESS_KEY: \${BACKUP_S3_SECRET_ACCESS_KEY:-}
1372
+ BACKUP_S3_ENDPOINT: \${BACKUP_S3_ENDPOINT:-}
1373
+ BACKUP_S3_PREFIX: \${BACKUP_S3_PREFIX:-}
1374
+ BACKUP_WEBDAV_URL: \${BACKUP_WEBDAV_URL:-}
1375
+ BACKUP_WEBDAV_USERNAME: \${BACKUP_WEBDAV_USERNAME:-}
1376
+ BACKUP_WEBDAV_PASSWORD: \${BACKUP_WEBDAV_PASSWORD:-}
1377
+ volumes:
1378
+ - uploads:/app/.uploads
1379
+ - backups:/backups
1380
+ depends_on:
1381
+ postgres:
1382
+ condition: service_healthy
1383
+ restart: 'no'
1384
+
1385
+ web:
1386
+ build: .
1387
+ image: ${name}
1388
+ restart: unless-stopped
1389
+ mem_limit: \${WEB_MEM_LIMIT:-1g}
1390
+ cpus: \${WEB_CPUS:-2}
1391
+ logging:
1392
+ driver: json-file
1393
+ options:
1394
+ max-size: 10m
1395
+ max-file: '3'
1396
+ ports:
1397
+ - '\${PORT:-127.0.0.1:3000}:3000'
1398
+ environment:
1399
+ DATABASE_URL: postgres://community:\${POSTGRES_PASSWORD:-community}@postgres:5432/community
1400
+ AUTH_SECRET: \${AUTH_SECRET:?AUTH_SECRET must be set}
1401
+ TICK_SECRET: \${TICK_SECRET:?TICK_SECRET must be set}
1402
+ QUEUE_DRIVER: postgres
1403
+ CACHE_DRIVER: \${CACHE_DRIVER:-next}
1404
+ REDIS_URL: \${REDIS_URL:-}
1405
+ FILESTORE_DRIVER: local
1406
+ APP_URL: \${APP_URL:-http://localhost:3000}
1407
+ # One reverse proxy (Caddy, in the guide's own walkthrough) sits in
1408
+ # front of \`web\` \u2014 see "Count your proxies" in
1409
+ # docs/getting-started/deployment/docker-compose.md.
1410
+ TRUSTED_PROXY_HOPS: \${TRUSTED_PROXY_HOPS:-1}
1411
+ # Leaving this at \`log\` does not mean no mail: it means the board
1412
+ # decides, from the installer on first run or from
1413
+ # /admin/settings?group=mail afterwards, with no redeploy. Setting it
1414
+ # here makes this file authoritative instead.
1415
+ MAIL_DRIVER: \${MAIL_DRIVER:-log}
1416
+ MAIL_FROM: \${MAIL_FROM:-}
1417
+ MAIL_HTTP_ENDPOINT: \${MAIL_HTTP_ENDPOINT:-}
1418
+ MAIL_HTTP_TOKEN: \${MAIL_HTTP_TOKEN:-}
1419
+ MAIL_SMTP_HOST: \${MAIL_SMTP_HOST:-}
1420
+ MAIL_SMTP_PORT: \${MAIL_SMTP_PORT:-}
1421
+ MAIL_SMTP_SECURITY: \${MAIL_SMTP_SECURITY:-}
1422
+ MAIL_SMTP_USERNAME: \${MAIL_SMTP_USERNAME:-}
1423
+ MAIL_SMTP_PASSWORD: \${MAIL_SMTP_PASSWORD:-}
1424
+ BACKUP_S3_BUCKET: \${BACKUP_S3_BUCKET:-}
1425
+ BACKUP_S3_REGION: \${BACKUP_S3_REGION:-}
1426
+ BACKUP_S3_ACCESS_KEY_ID: \${BACKUP_S3_ACCESS_KEY_ID:-}
1427
+ BACKUP_S3_SECRET_ACCESS_KEY: \${BACKUP_S3_SECRET_ACCESS_KEY:-}
1428
+ BACKUP_S3_ENDPOINT: \${BACKUP_S3_ENDPOINT:-}
1429
+ BACKUP_S3_PREFIX: \${BACKUP_S3_PREFIX:-}
1430
+ BACKUP_WEBDAV_URL: \${BACKUP_WEBDAV_URL:-}
1431
+ BACKUP_WEBDAV_USERNAME: \${BACKUP_WEBDAV_USERNAME:-}
1432
+ BACKUP_WEBDAV_PASSWORD: \${BACKUP_WEBDAV_PASSWORD:-}
1433
+ volumes:
1434
+ - uploads:/app/.uploads
1435
+ - backups:/backups
1436
+ depends_on:
1437
+ postgres:
1438
+ condition: service_healthy
1439
+ migrate:
1440
+ condition: service_completed_successfully
1441
+
1442
+ # @meith/worker is not published (see the meith repository's
1443
+ # docs/contributing/release.md), so there is no compiled worker binary
1444
+ # this board can run \u2014 a small loop calling /api/system/tick instead, the
1445
+ # same shape docker-compose.yaml and docker-compose.prebuilt.yaml use.
1446
+ worker:
1447
+ image: alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
1448
+ restart: unless-stopped
1449
+ mem_limit: \${WORKER_MEM_LIMIT:-64m}
1450
+ cpus: \${WORKER_CPUS:-0.25}
1451
+ logging:
1452
+ driver: json-file
1453
+ options:
1454
+ max-size: 10m
1455
+ max-file: '3'
1456
+ environment:
1457
+ TICK_SECRET: \${TICK_SECRET:?TICK_SECRET must be set}
1458
+ command:
1459
+ - sh
1460
+ - -c
1461
+ - |
1462
+ apk add --no-cache curl >/dev/null
1463
+ while true; do
1464
+ curl -fsS -m 55 -H "Authorization: Bearer $$TICK_SECRET" \\
1465
+ http://web:3000/api/system/tick >/dev/null 2>&1 \\
1466
+ || echo "tick failed at $$(date -Is)"
1467
+ sleep 60
1468
+ done
1469
+ depends_on:
1470
+ - web
1471
+
1472
+ # Off by default \u2014 see docs/guides/operations/scaling.md before setting
1473
+ # CACHE_DRIVER=redis above. This is the server it needs.
1474
+ redis:
1475
+ profiles: ['redis']
1476
+ image: valkey/valkey:9-alpine@sha256:a174b894902bd3367e330d47cc2054367dc4917701776aaf336f41d83b65ec7a
1477
+ restart: unless-stopped
1478
+ mem_limit: \${REDIS_MEM_LIMIT:-256m}
1479
+ cpus: \${REDIS_CPUS:-0.5}
1480
+ logging:
1481
+ driver: json-file
1482
+ options:
1483
+ max-size: 10m
1484
+ max-file: '3'
1485
+ healthcheck:
1486
+ test: ['CMD', 'valkey-cli', 'ping']
1487
+ interval: 10s
1488
+ timeout: 5s
1489
+ retries: 5
1490
+
1295
1491
  volumes:
1296
1492
  pgdata:
1297
1493
  uploads:
@@ -1306,11 +1502,13 @@ A forum, built on [Meith](${repositoryUrl}).
1306
1502
 
1307
1503
  ## Deploy
1308
1504
 
1309
- Two paths onto [Coolify](https://coolify.io), both ending at the same
1310
- \`/install\`. **Quick start** is the default and needs nothing but a push;
1311
- **advanced/prebuilt** moves the build off the server, onto GitHub Actions, for
1312
- a low-spec build server or a faster deploy. Pick one \u2014 a board only ever runs
1313
- one of them at a time.
1505
+ Three paths onto a server, all ending at the same \`/install\`. **Quick
1506
+ start** onto [Coolify](https://coolify.io) is the default and needs nothing
1507
+ but a push; **advanced/prebuilt** moves the build off the server, onto
1508
+ GitHub Actions, for a low-spec build server or a faster deploy; **without a
1509
+ panel** is the same four containers run by hand, with your own \`.env\` and
1510
+ reverse proxy, and no Coolify at all. Pick one \u2014 a board only ever runs one
1511
+ of them at a time.
1314
1512
 
1315
1513
  ### Quick start (default)
1316
1514
 
@@ -1398,13 +1596,20 @@ rather not use GitHub Actions for the build \u2014 push the result wherever
1398
1596
  docker build -f Dockerfile.prebuilt --build-arg MEITH_VERSION=$(node -p "require('./package.json').dependencies['@meith/web']") -t ${name} .
1399
1597
  \`\`\`
1400
1598
 
1401
- **Without a panel**: [docs/getting-started/deployment/docker-compose.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/docker-compose.md)
1402
- is the same four containers by hand \u2014 your own \`.env\`, a reverse proxy you
1403
- already run, no Coolify. \`Dockerfile\` and \`docker-compose.yaml\` here are this
1404
- board's own version of exactly that shape (or \`Dockerfile.prebuilt\` and
1405
- \`docker-compose.prebuilt.yaml\`, for the advanced path).
1599
+ ### Without a panel
1600
+
1601
+ \`docker-compose.byhand.yaml\`, beside the two Coolify files above, is the same
1602
+ four containers deployed with nothing generating secrets for you: a \`.env\`
1603
+ you write yourself, a port published for the reverse proxy you already run,
1604
+ and \`docker compose up -d --build\` in place of a panel's Deploy button.
1605
+ [docs/getting-started/deployment/docker-compose.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/docker-compose.md)
1606
+ is the full walkthrough this file is the last step of, including the
1607
+ \`.env\` this repository does not carry \u2014 nothing here belongs in git. Delete
1608
+ this file if you know you will only ever deploy through Coolify; keep it,
1609
+ and it needs nothing else changed, if you later want to move away from
1610
+ Coolify without changing how the board itself is built.
1406
1611
 
1407
- Two things nothing configures for you, on either path:
1612
+ Two things nothing configures for you, on any path:
1408
1613
 
1409
1614
  - **Mail.** Until \`MAIL_DRIVER\` and its three settings exist, every message is
1410
1615
  written to the log and delivered to nobody, so password reset fails silently.
@@ -2829,7 +3034,7 @@ async function run(argv, version, updateOptions = {}) {
2829
3034
  }
2830
3035
 
2831
3036
  // src/bin.ts
2832
- var result = await run(process.argv.slice(2), "0.34.0");
3037
+ var result = await run(process.argv.slice(2), "0.35.1");
2833
3038
  for (const line of result.lines) {
2834
3039
  if (result.code === 0) console.log(line);
2835
3040
  else console.error(line);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-meith",
3
- "version": "0.34.0",
3
+ "version": "0.35.1",
4
4
  "description": "Scaffold a Meith board, plugin or theme — npx create-meith <name> writes a deployable board workspace; --plugin and --theme write extension workspaces built on the published kits.",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/bin.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { run } from './cli'
3
3
 
4
- const result = await run(process.argv.slice(2), '0.34.0')
4
+ const result = await run(process.argv.slice(2), '0.35.1')
5
5
  for (const line of result.lines) {
6
6
  if (result.code === 0) console.log(line)
7
7
  else console.error(line)
package/src/scaffold.ts CHANGED
@@ -377,6 +377,7 @@ const SELF_HOST_DEPLOY_KIT = [
377
377
  'Dockerfile.prebuilt',
378
378
  'docker-compose.yaml',
379
379
  'docker-compose.prebuilt.yaml',
380
+ 'docker-compose.byhand.yaml',
380
381
  'docker-entrypoint.sh',
381
382
  'docker-healthcheck.sh',
382
383
  ] as const
@@ -1341,6 +1342,202 @@ services:
1341
1342
  depends_on:
1342
1343
  - web
1343
1344
 
1345
+ volumes:
1346
+ pgdata:
1347
+ uploads:
1348
+ backups:
1349
+ `,
1350
+ )
1351
+
1352
+ files.set(
1353
+ 'docker-compose.byhand.yaml',
1354
+ `# ${name}, deployed by Docker Compose without a panel — your own \`.env\`,
1355
+ # your own reverse proxy, no Coolify. The same four services as
1356
+ # docker-compose.yaml and docker-compose.prebuilt.yaml above it (db,
1357
+ # migrate, web, worker), reshaped for a machine with nothing generating
1358
+ # secrets for you: every value Coolify would have filled in — the database
1359
+ # password, AUTH_SECRET, TICK_SECRET, the board's own address — comes from
1360
+ # a \`.env\` beside this file instead. See the meith repository's
1361
+ # docs/getting-started/deployment/docker-compose.md, which this file is
1362
+ # the last step of.
1363
+ #
1364
+ # \`docker compose\` only auto-discovers a file literally named
1365
+ # docker-compose.yaml, and that name is already spoken for — Coolify's own
1366
+ # zero-configuration default. Put \`COMPOSE_FILE=docker-compose.byhand.yaml\`
1367
+ # in \`.env\` once, and every plain \`docker compose ...\` command reads this
1368
+ # file without a \`-f\`, including the ones the rest of this project's
1369
+ # documentation already shows you.
1370
+ #
1371
+ # \`docker compose up -d --build\` builds \`Dockerfile\` from this repository,
1372
+ # on this machine — the same trade the quick-start path above takes. For a
1373
+ # low-spec server, build \`Dockerfile.prebuilt\` somewhere else instead (push
1374
+ # to GitHub and let \`.github/workflows/build.yml\` do it, or run
1375
+ # \`docker build -f Dockerfile.prebuilt ...\` by hand) and change the two
1376
+ # \`build: .\` lines below to \`image: <that image>:<version>\` — the
1377
+ # substitution docs/getting-started/deployment/docker-compose.md, "Building
1378
+ # somewhere else", walks through.
1379
+ services:
1380
+ postgres:
1381
+ image: postgres:18-alpine@sha256:d3e1620b530c944afa6e887d22eb899824da68e19c52024bf98f5220c88a65b2
1382
+ restart: unless-stopped
1383
+ mem_limit: \${POSTGRES_MEM_LIMIT:-1g}
1384
+ cpus: \${POSTGRES_CPUS:-1}
1385
+ logging:
1386
+ driver: json-file
1387
+ options:
1388
+ max-size: 10m
1389
+ max-file: '3'
1390
+ environment:
1391
+ POSTGRES_USER: community
1392
+ # Generate with \`openssl rand -hex 32\`, not base64 — this value is
1393
+ # substituted into the postgres:// URL below, and base64's alphabet
1394
+ # includes \`/\` and \`+\`, which make the connection string unparseable.
1395
+ POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-community}
1396
+ POSTGRES_DB: community
1397
+ volumes:
1398
+ - pgdata:/var/lib/postgresql
1399
+ healthcheck:
1400
+ test: ['CMD-SHELL', 'pg_isready -U community -d community']
1401
+ interval: 10s
1402
+ timeout: 5s
1403
+ retries: 5
1404
+
1405
+ # Runs to completion, then exits. web waits for it, so the schema is
1406
+ # always applied before the first request rather than racing it.
1407
+ migrate:
1408
+ build: .
1409
+ image: ${name}
1410
+ environment:
1411
+ MEITH_ROLE: migrate
1412
+ DATABASE_URL: postgres://community:\${POSTGRES_PASSWORD:-community}@postgres:5432/community
1413
+ AUTH_SECRET: \${AUTH_SECRET:?AUTH_SECRET must be set}
1414
+ TICK_SECRET: \${TICK_SECRET:?TICK_SECRET must be set}
1415
+ FILESTORE_DRIVER: local
1416
+ # So that "back up before migrating" (a board setting) can ship the
1417
+ # bundle it takes to the same off-site destination web uses.
1418
+ BACKUP_S3_BUCKET: \${BACKUP_S3_BUCKET:-}
1419
+ BACKUP_S3_REGION: \${BACKUP_S3_REGION:-}
1420
+ BACKUP_S3_ACCESS_KEY_ID: \${BACKUP_S3_ACCESS_KEY_ID:-}
1421
+ BACKUP_S3_SECRET_ACCESS_KEY: \${BACKUP_S3_SECRET_ACCESS_KEY:-}
1422
+ BACKUP_S3_ENDPOINT: \${BACKUP_S3_ENDPOINT:-}
1423
+ BACKUP_S3_PREFIX: \${BACKUP_S3_PREFIX:-}
1424
+ BACKUP_WEBDAV_URL: \${BACKUP_WEBDAV_URL:-}
1425
+ BACKUP_WEBDAV_USERNAME: \${BACKUP_WEBDAV_USERNAME:-}
1426
+ BACKUP_WEBDAV_PASSWORD: \${BACKUP_WEBDAV_PASSWORD:-}
1427
+ volumes:
1428
+ - uploads:/app/.uploads
1429
+ - backups:/backups
1430
+ depends_on:
1431
+ postgres:
1432
+ condition: service_healthy
1433
+ restart: 'no'
1434
+
1435
+ web:
1436
+ build: .
1437
+ image: ${name}
1438
+ restart: unless-stopped
1439
+ mem_limit: \${WEB_MEM_LIMIT:-1g}
1440
+ cpus: \${WEB_CPUS:-2}
1441
+ logging:
1442
+ driver: json-file
1443
+ options:
1444
+ max-size: 10m
1445
+ max-file: '3'
1446
+ ports:
1447
+ - '\${PORT:-127.0.0.1:3000}:3000'
1448
+ environment:
1449
+ DATABASE_URL: postgres://community:\${POSTGRES_PASSWORD:-community}@postgres:5432/community
1450
+ AUTH_SECRET: \${AUTH_SECRET:?AUTH_SECRET must be set}
1451
+ TICK_SECRET: \${TICK_SECRET:?TICK_SECRET must be set}
1452
+ QUEUE_DRIVER: postgres
1453
+ CACHE_DRIVER: \${CACHE_DRIVER:-next}
1454
+ REDIS_URL: \${REDIS_URL:-}
1455
+ FILESTORE_DRIVER: local
1456
+ APP_URL: \${APP_URL:-http://localhost:3000}
1457
+ # One reverse proxy (Caddy, in the guide's own walkthrough) sits in
1458
+ # front of \`web\` — see "Count your proxies" in
1459
+ # docs/getting-started/deployment/docker-compose.md.
1460
+ TRUSTED_PROXY_HOPS: \${TRUSTED_PROXY_HOPS:-1}
1461
+ # Leaving this at \`log\` does not mean no mail: it means the board
1462
+ # decides, from the installer on first run or from
1463
+ # /admin/settings?group=mail afterwards, with no redeploy. Setting it
1464
+ # here makes this file authoritative instead.
1465
+ MAIL_DRIVER: \${MAIL_DRIVER:-log}
1466
+ MAIL_FROM: \${MAIL_FROM:-}
1467
+ MAIL_HTTP_ENDPOINT: \${MAIL_HTTP_ENDPOINT:-}
1468
+ MAIL_HTTP_TOKEN: \${MAIL_HTTP_TOKEN:-}
1469
+ MAIL_SMTP_HOST: \${MAIL_SMTP_HOST:-}
1470
+ MAIL_SMTP_PORT: \${MAIL_SMTP_PORT:-}
1471
+ MAIL_SMTP_SECURITY: \${MAIL_SMTP_SECURITY:-}
1472
+ MAIL_SMTP_USERNAME: \${MAIL_SMTP_USERNAME:-}
1473
+ MAIL_SMTP_PASSWORD: \${MAIL_SMTP_PASSWORD:-}
1474
+ BACKUP_S3_BUCKET: \${BACKUP_S3_BUCKET:-}
1475
+ BACKUP_S3_REGION: \${BACKUP_S3_REGION:-}
1476
+ BACKUP_S3_ACCESS_KEY_ID: \${BACKUP_S3_ACCESS_KEY_ID:-}
1477
+ BACKUP_S3_SECRET_ACCESS_KEY: \${BACKUP_S3_SECRET_ACCESS_KEY:-}
1478
+ BACKUP_S3_ENDPOINT: \${BACKUP_S3_ENDPOINT:-}
1479
+ BACKUP_S3_PREFIX: \${BACKUP_S3_PREFIX:-}
1480
+ BACKUP_WEBDAV_URL: \${BACKUP_WEBDAV_URL:-}
1481
+ BACKUP_WEBDAV_USERNAME: \${BACKUP_WEBDAV_USERNAME:-}
1482
+ BACKUP_WEBDAV_PASSWORD: \${BACKUP_WEBDAV_PASSWORD:-}
1483
+ volumes:
1484
+ - uploads:/app/.uploads
1485
+ - backups:/backups
1486
+ depends_on:
1487
+ postgres:
1488
+ condition: service_healthy
1489
+ migrate:
1490
+ condition: service_completed_successfully
1491
+
1492
+ # @meith/worker is not published (see the meith repository's
1493
+ # docs/contributing/release.md), so there is no compiled worker binary
1494
+ # this board can run — a small loop calling /api/system/tick instead, the
1495
+ # same shape docker-compose.yaml and docker-compose.prebuilt.yaml use.
1496
+ worker:
1497
+ image: alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
1498
+ restart: unless-stopped
1499
+ mem_limit: \${WORKER_MEM_LIMIT:-64m}
1500
+ cpus: \${WORKER_CPUS:-0.25}
1501
+ logging:
1502
+ driver: json-file
1503
+ options:
1504
+ max-size: 10m
1505
+ max-file: '3'
1506
+ environment:
1507
+ TICK_SECRET: \${TICK_SECRET:?TICK_SECRET must be set}
1508
+ command:
1509
+ - sh
1510
+ - -c
1511
+ - |
1512
+ apk add --no-cache curl >/dev/null
1513
+ while true; do
1514
+ curl -fsS -m 55 -H "Authorization: Bearer $$TICK_SECRET" \\
1515
+ http://web:3000/api/system/tick >/dev/null 2>&1 \\
1516
+ || echo "tick failed at $$(date -Is)"
1517
+ sleep 60
1518
+ done
1519
+ depends_on:
1520
+ - web
1521
+
1522
+ # Off by default — see docs/guides/operations/scaling.md before setting
1523
+ # CACHE_DRIVER=redis above. This is the server it needs.
1524
+ redis:
1525
+ profiles: ['redis']
1526
+ image: valkey/valkey:9-alpine@sha256:a174b894902bd3367e330d47cc2054367dc4917701776aaf336f41d83b65ec7a
1527
+ restart: unless-stopped
1528
+ mem_limit: \${REDIS_MEM_LIMIT:-256m}
1529
+ cpus: \${REDIS_CPUS:-0.5}
1530
+ logging:
1531
+ driver: json-file
1532
+ options:
1533
+ max-size: 10m
1534
+ max-file: '3'
1535
+ healthcheck:
1536
+ test: ['CMD', 'valkey-cli', 'ping']
1537
+ interval: 10s
1538
+ timeout: 5s
1539
+ retries: 5
1540
+
1344
1541
  volumes:
1345
1542
  pgdata:
1346
1543
  uploads:
@@ -1356,11 +1553,13 @@ A forum, built on [Meith](${repositoryUrl}).
1356
1553
 
1357
1554
  ## Deploy
1358
1555
 
1359
- Two paths onto [Coolify](https://coolify.io), both ending at the same
1360
- \`/install\`. **Quick start** is the default and needs nothing but a push;
1361
- **advanced/prebuilt** moves the build off the server, onto GitHub Actions, for
1362
- a low-spec build server or a faster deploy. Pick one — a board only ever runs
1363
- one of them at a time.
1556
+ Three paths onto a server, all ending at the same \`/install\`. **Quick
1557
+ start** onto [Coolify](https://coolify.io) is the default and needs nothing
1558
+ but a push; **advanced/prebuilt** moves the build off the server, onto
1559
+ GitHub Actions, for a low-spec build server or a faster deploy; **without a
1560
+ panel** is the same four containers run by hand, with your own \`.env\` and
1561
+ reverse proxy, and no Coolify at all. Pick one — a board only ever runs one
1562
+ of them at a time.
1364
1563
 
1365
1564
  ### Quick start (default)
1366
1565
 
@@ -1448,13 +1647,20 @@ rather not use GitHub Actions for the build — push the result wherever
1448
1647
  docker build -f Dockerfile.prebuilt --build-arg MEITH_VERSION=$(node -p "require('./package.json').dependencies['@meith/web']") -t ${name} .
1449
1648
  \`\`\`
1450
1649
 
1451
- **Without a panel**: [docs/getting-started/deployment/docker-compose.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/docker-compose.md)
1452
- is the same four containers by hand — your own \`.env\`, a reverse proxy you
1453
- already run, no Coolify. \`Dockerfile\` and \`docker-compose.yaml\` here are this
1454
- board's own version of exactly that shape (or \`Dockerfile.prebuilt\` and
1455
- \`docker-compose.prebuilt.yaml\`, for the advanced path).
1650
+ ### Without a panel
1651
+
1652
+ \`docker-compose.byhand.yaml\`, beside the two Coolify files above, is the same
1653
+ four containers deployed with nothing generating secrets for you: a \`.env\`
1654
+ you write yourself, a port published for the reverse proxy you already run,
1655
+ and \`docker compose up -d --build\` in place of a panel's Deploy button.
1656
+ [docs/getting-started/deployment/docker-compose.md](${repositoryUrl}/blob/main/docs/getting-started/deployment/docker-compose.md)
1657
+ is the full walkthrough this file is the last step of, including the
1658
+ \`.env\` this repository does not carry — nothing here belongs in git. Delete
1659
+ this file if you know you will only ever deploy through Coolify; keep it,
1660
+ and it needs nothing else changed, if you later want to move away from
1661
+ Coolify without changing how the board itself is built.
1456
1662
 
1457
- Two things nothing configures for you, on either path:
1663
+ Two things nothing configures for you, on any path:
1458
1664
 
1459
1665
  - **Mail.** Until \`MAIL_DRIVER\` and its three settings exist, every message is
1460
1666
  written to the log and delivered to nobody, so password reset fails silently.