See the best posts from any Bluesky account
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Follow AdonisJS deployment best practices for Docker and HTTP config

- Dockerfile: use self-contained build output as app root with clean prod-only
pnpm install, explicit NODE_ENV=development for build deps, shared base stage
- config/app.ts: add keepAliveTimeout (55s) to prevent 502s behind reverse proxies
- docker-compose: add ClickHouse healthcheck so web waits for readiness before
migrations, pass HEALTH_CHECK_TOKEN header in web healthcheck, use 127.0.0.1
to avoid Alpine IPv6 localhost resolution, update commands for new WORKDIR

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+40 -19
+21 -14
Dockerfile
··· 1 - FROM node:24-alpine AS build 1 + FROM node:24-alpine AS base 2 + RUN npm install -g pnpm@10 3 + 4 + # --------------------------------------------------------------------------- 5 + # Stage 1: install all deps + build 6 + # --------------------------------------------------------------------------- 7 + FROM base AS build 2 8 WORKDIR /app 3 9 4 - # Install pnpm and native build toolchain (for better-sqlite3, @swc/core, etc.) 5 - RUN npm install -g pnpm@10 \ 6 - && apk add --no-cache python3 make g++ 10 + # Native build toolchain (for better-sqlite3, @swc/core, etc.) 11 + RUN apk add --no-cache python3 make g++ 7 12 8 13 # Copy package manifest + lockfile for layer caching 9 14 COPY package.json pnpm-lock.yaml ./ 10 - RUN pnpm install --frozen-lockfile 15 + RUN NODE_ENV=development pnpm install --frozen-lockfile 11 16 12 17 # Copy source and build the Adonis app 13 18 COPY . . 14 19 RUN node ace build 15 20 16 - # Prune dev dependencies so the runtime stage only copies prod node_modules 17 - RUN pnpm prune --prod 18 - 19 - FROM node:24-alpine AS runtime 21 + # --------------------------------------------------------------------------- 22 + # Stage 2: production image — install prod-only deps inside build output 23 + # --------------------------------------------------------------------------- 24 + FROM base AS runtime 20 25 RUN apk add --no-cache tini 21 26 WORKDIR /app 22 27 23 - # Copy compiled build output + pruned prod node_modules from build stage. 24 - # Adonis's build is self-contained under build/; node_modules sits alongside 25 - # and is resolved by Node's module lookup from build/bin/*. 26 - COPY --from=build --chown=node:node /app/build ./build 27 - COPY --from=build --chown=node:node /app/node_modules ./node_modules 28 + # The build directory is self-contained (JS + package.json + lockfile). 29 + # Copy it as the application root and install only production dependencies. 30 + COPY --from=build --chown=node:node /app/build ./ 31 + RUN pnpm install --frozen-lockfile --prod 32 + 33 + # Ensure the SQLite data directory exists and is writable by the non-root user 34 + RUN mkdir -p /data && chown node:node /data 28 35 29 36 USER node 30 37 ENTRYPOINT ["/sbin/tini", "--"]
+5
config/app.ts
··· 14 14 */ 15 15 export const http = defineConfig({ 16 16 /** 17 + * Prevent 502 errors when behind a reverse proxy or load balancer. 18 + * Must be higher than the proxy's own timeout (typically 60s). 19 + */ 20 + keepAliveTimeout: 55000, 21 + /** 17 22 * Generate a unique request ID for each incoming HTTP request. 18 23 * Useful for request tracing and debugging in logs. 19 24 */
+14 -5
docker-compose.yml
··· 11 11 - "127.0.0.1:8123:8123" 12 12 ulimits: 13 13 nofile: 262144 14 + healthcheck: 15 + test: ["CMD", "wget", "-qO-", "http://localhost:8123/ping"] 16 + interval: 2s 17 + timeout: 3s 18 + retries: 15 19 + start_period: 5s 14 20 15 21 web: 16 22 build: . 17 - command: sh -c "node build/ace.js migration:run --force && node build/ace.js clickhouse:migrate && node build/bin/server.js" 18 - depends_on: [clickhouse] 23 + command: sh -c "node ace.js migration:run --force && node ace.js clickhouse:migrate && node bin/server.js" 24 + depends_on: 25 + clickhouse: 26 + condition: service_healthy 19 27 volumes: 20 28 - sqlite-data:/data 21 29 environment: ··· 33 41 LOG_LEVEL: info 34 42 SESSION_DRIVER: cookie 35 43 QUEUE_DRIVER: database 44 + HEALTH_CHECK_TOKEN: ${HEALTH_CHECK_TOKEN:-} 36 45 ports: 37 46 - "3333:3333" 38 47 healthcheck: 39 - test: ["CMD", "wget", "-qO-", "http://localhost:3333/health/ready"] 48 + test: ["CMD", "wget", "-qO-", "--header=x-monitoring-secret:$HEALTH_CHECK_TOKEN", "http://127.0.0.1:3333/health/ready"] 40 49 interval: 2s 41 50 timeout: 3s 42 51 retries: 30 ··· 44 53 45 54 jetstream-worker: 46 55 build: . 47 - command: node build/ace.js jetstream:consume 56 + command: node ace.js jetstream:consume 48 57 depends_on: 49 58 clickhouse: 50 59 condition: service_started ··· 69 78 70 79 queue-worker: 71 80 build: . 72 - command: node build/ace.js queue:work 81 + command: node ace.js queue:work 73 82 depends_on: 74 83 clickhouse: 75 84 condition: service_started