Add Dockerfile and Docker Compose for Next.js deployment - #18
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…dpoint for Next.js Docker deployment Co-authored-by: phodal <472311+phodal@users.noreply.github.com>
|
Augment review |
🤖 Augment PR SummarySummary: Adds first-class Docker support for the Routa.js Next.js app to enable containerized deployment and easier service verification. Changes:
Technical Notes: The image defaults to SQLite via 🤖 Was this summary useful? React with 👍 or 👎 |
| - "3000:3000" | ||
| environment: | ||
| # SQLite mode (default) — no external DB required | ||
| ROUTA_DB_DRIVER: ${ROUTA_DB_DRIVER:-sqlite} |
There was a problem hiding this comment.
Because getDatabaseDriver() treats ROUTA_DB_DRIVER as the highest-priority override, defaulting it to sqlite here means setting/uncommenting DATABASE_URL alone won’t switch the app to Postgres (despite the comment). Consider aligning the Compose/README guidance so the Postgres profile actually selects the Postgres driver.
Severity: medium
Other Locations
Dockerfile:35README.md:96
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| # Uncomment (or set in .env) to switch to Postgres: | ||
| # DATABASE_URL: ${DATABASE_URL} | ||
| volumes: | ||
| - routa_data:/app/data |
There was a problem hiding this comment.
Since the container runs as non-root (nextjs), mounting a fresh named volume at /app/data can result in the directory being root-owned and not writable, preventing SQLite from creating /app/data/routa.db. It may be worth ensuring the mounted volume path is writable by uid 1001 at runtime.
Severity: medium
Other Locations
Dockerfile:47
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
There was a problem hiding this comment.
Pull request overview
Adds first-class containerization support for the Next.js app so it can be built, run, and health-checked via Docker/Docker Compose, including an optional Postgres sidecar mode.
Changes:
- Adds multi-stage
Dockerfileproducing a Next.js standalone runner image. - Adds
docker-compose.ymlwith default (self-contained) mode and apostgresprofile. - Introduces
GET /api/healthendpoint and documents it inapi-contract.yaml; adds Docker quickstart docs and abuild:dockerscript.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
src/app/api/health/route.ts |
Adds a lightweight health endpoint for container health checks. |
package.json |
Adds build:docker to explicitly build Next.js in standalone mode. |
package-lock.json |
Lockfile metadata updates (dev flags). |
docker-compose.yml |
Defines app service + optional Postgres service and a healthcheck. |
api-contract.yaml |
Documents the new /api/health endpoint. |
README.md |
Adds Docker quickstart instructions. |
Dockerfile |
Multi-stage Docker build targeting a minimal runtime image. |
.dockerignore |
Excludes build artifacts and local files from Docker build context. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export const dynamic = "force-dynamic"; | ||
|
|
||
| export function GET() { | ||
| return NextResponse.json({ status: "ok", timestamp: new Date().toISOString() }); |
There was a problem hiding this comment.
Many existing API routes set an explicit Cache-Control: no-store header on JSON responses (e.g. src/app/api/sessions/route.ts:13-18, src/app/api/a2a/card/route.ts:24-29). To keep behavior consistent and prevent any intermediary caching of a stale “ok”, consider adding the same header to this health response.
| return NextResponse.json({ status: "ok", timestamp: new Date().toISOString() }); | |
| return NextResponse.json( | |
| { status: "ok", timestamp: new Date().toISOString() }, | |
| { headers: { "Cache-Control": "no-store" } }, | |
| ); |
There was a problem hiding this comment.
Added Cache-Control: no-store to the health endpoint response in e938237, consistent with the pattern used by /api/sessions and /api/a2a/card.
|
|
||
| # Build in standalone mode so the output is self-contained. | ||
| # `build:docker` sets ROUTA_DESKTOP_STANDALONE=1 which enables `output: "standalone"` in next.config.ts. | ||
| RUN npm run build:docker |
There was a problem hiding this comment.
Docker image defaults to SQLite, but the Next.js build used here only runs next build in standalone mode. In this codebase, SQLite support is loaded via eval("require")("./db/sqlite") / ./db/sqlite-stores and those TS sources are excluded from tsconfig and only compiled into .next-*/server/chunks/db/* by scripts/build-desktop-bundle.mjs. As a result, the Docker standalone output is likely missing the db/sqlite* chunk modules and createSqliteSystem() will fall back to in-memory stores (no persistence) even though ROUTA_DB_DRIVER=sqlite is set. Consider adding an explicit build step in the Docker build to compile/copy the SQLite chunk modules into the standalone output (or otherwise ensure sqlite.ts/sqlite-stores.ts are present at runtime).
| RUN npm run build:docker | |
| RUN npm run build:docker && node scripts/build-desktop-bundle.mjs |
There was a problem hiding this comment.
Added scripts/build-docker.mjs (e938237) which runs after next build as part of npm run build:docker. It compiles sqlite.ts, sqlite-schema.ts, and sqlite-stores.ts via esbuild into .next/standalone/.next/server/chunks/db/ — the path where eval("require")("./db/sqlite") resolves at runtime — and copies better-sqlite3 (with its native addon) into the standalone node_modules. This mirrors the approach in build-desktop-bundle.mjs but targets the Docker standalone output.
| # SQLite mode (default) — no external DB required | ||
| ROUTA_DB_DRIVER: ${ROUTA_DB_DRIVER:-sqlite} | ||
| ROUTA_DB_PATH: /app/data/routa.db | ||
| # Uncomment (or set in .env) to switch to Postgres: | ||
| # DATABASE_URL: ${DATABASE_URL} | ||
| volumes: | ||
| - routa_data:/app/data |
There was a problem hiding this comment.
The app service is configured to run in SQLite mode by default (ROUTA_DB_DRIVER/ROUTA_DB_PATH), but in this repo SQLite stores are dynamically required and may be absent from a plain Next.js standalone build (see createSqliteSystem() fallback-to-in-memory behavior). If the Docker build doesn’t package the compiled db/sqlite* chunk modules, this compose setup will appear to work but won’t persist data. Either ensure the image build includes the SQLite chunk modules or adjust the default mode here to match what the container actually supports.
| # SQLite mode (default) — no external DB required | |
| ROUTA_DB_DRIVER: ${ROUTA_DB_DRIVER:-sqlite} | |
| ROUTA_DB_PATH: /app/data/routa.db | |
| # Uncomment (or set in .env) to switch to Postgres: | |
| # DATABASE_URL: ${DATABASE_URL} | |
| volumes: | |
| - routa_data:/app/data | |
| # Default to in-memory store unless a specific driver is configured. | |
| # This avoids advertising SQLite persistence when the image may not | |
| # include the dynamic SQLite chunks (see createSqliteSystem()). | |
| ROUTA_DB_DRIVER: ${ROUTA_DB_DRIVER:-memory} | |
| # To use SQLite persistence, ensure the image includes the SQLite | |
| # chunk modules and then uncomment the following line (and the | |
| # routa_data volume mount below): | |
| # ROUTA_DB_PATH: /app/data/routa.db | |
| # Uncomment (or set in .env) to switch to Postgres: | |
| # DATABASE_URL: ${DATABASE_URL} | |
| volumes: | |
| # Optional: enable this when using SQLite persistence with ROUTA_DB_PATH. | |
| # - routa_data:/app/data |
There was a problem hiding this comment.
The Docker build now properly bundles the SQLite chunk modules via scripts/build-docker.mjs (e938237), so ROUTA_DB_DRIVER=sqlite will find the compiled modules at runtime rather than silently falling back to in-memory.
| # SQLite mode (default) — no external DB required | ||
| ROUTA_DB_DRIVER: ${ROUTA_DB_DRIVER:-sqlite} | ||
| ROUTA_DB_PATH: /app/data/routa.db | ||
| # Uncomment (or set in .env) to switch to Postgres: | ||
| # DATABASE_URL: ${DATABASE_URL} | ||
| volumes: | ||
| - routa_data:/app/data | ||
| restart: unless-stopped | ||
| healthcheck: | ||
| test: ["CMD-SHELL", "wget -qO- http://localhost:3000/api/health || exit 1"] | ||
| interval: 30s | ||
| timeout: 10s | ||
| retries: 3 | ||
| start_period: 30s |
There was a problem hiding this comment.
--profile postgres starts a Postgres container, but the app service is not configured to actually use it (no DATABASE_URL passed through, and ROUTA_DB_DRIVER defaults to sqlite). As written, docker compose --profile postgres up will still run the app in SQLite mode and leave Postgres unused. To make the profile meaningful, wire DATABASE_URL (e.g. pointing at postgres:5432) and/or set ROUTA_DB_DRIVER=postgres under that profile, and consider depends_on with service_healthy so the app doesn’t race Postgres startup.
There was a problem hiding this comment.
Fixed in e938237. The app service now has DATABASE_URL: ${DATABASE_URL:-} so it's picked up from .env. Added depends_on: postgres: condition: service_healthy, required: false so the app waits for Postgres to be ready when the postgres profile is active, but doesn't fail when it's not running.
| # The DATABASE_URL must be passed via an .env file or the environment section. | ||
| # Example: create a .env file with DATABASE_URL=postgresql://routa:routa_secret@postgres:5432/routa | ||
| # then enable the postgres profile and uncomment DATABASE_URL in docker-compose.yml: |
There was a problem hiding this comment.
The Docker quickstart says Postgres mode works with --profile postgres, but it also requires manually “uncomment DATABASE_URL in docker-compose.yml”. This is easy to miss and, combined with the current compose file, results in the app still using SQLite even though Postgres is running. Consider updating the instructions (or the compose file) so --profile postgres actually configures DATABASE_URL/ROUTA_DB_DRIVER without requiring manual edits.
| # The DATABASE_URL must be passed via an .env file or the environment section. | |
| # Example: create a .env file with DATABASE_URL=postgresql://routa:routa_secret@postgres:5432/routa | |
| # then enable the postgres profile and uncomment DATABASE_URL in docker-compose.yml: | |
| # The DATABASE_URL (and optionally ROUTA_DB_DRIVER) must be passed via an .env file or the environment section. | |
| # Example .env: | |
| # DATABASE_URL=postgresql://routa:routa_secret@postgres:5432/routa | |
| # ROUTA_DB_DRIVER=postgres |
There was a problem hiding this comment.
Updated README in e938237 with a .env example showing ROUTA_DB_DRIVER=postgres, DATABASE_URL, and POSTGRES_PASSWORD — no manual edits to docker-compose.yml required.
|
@copilot apply changes based on the comments in this thread |
… postgres profile wiring, README clarity Co-authored-by: phodal <472311+phodal@users.noreply.github.com>
* feat(docker): add local Docker dev environment with isolated SQLite - Add docker-compose.dev.yml with port 3001:3000, routa_dev_data volume, healthcheck, and restart policy - Add .env.docker template with ANTHROPIC_AUTH_TOKEN, PLATFORM, and optional GITHUB_* variables with Chinese comments - Add 5 npm scripts: docker:dev, docker:dev:build, docker:dev:down, docker:dev:logs, docker:dev:ps - Add .env.docker to .gitignore * fix(docker): add .env.docker.example template for version control The .env.docker file is excluded by .gitignore (correctly, for user secrets), but new developers need a template to copy from. This commit adds the example file that can be committed to git while keeping user configs out of version control. --------- Co-authored-by: Kiro AI <kiro@kiro.dev>
No Docker support existed for the Next.js app, making containerized deployment and service verification impossible.
Changes
Dockerfile— Multi-stage build (deps → builder → runner) using Next.js standalone output (ROUTA_DESKTOP_STANDALONE=1). Defaults to SQLite so no external DB is required. Runs as non-rootnextjsuser.docker-compose.yml— Two modes:docker compose up)--profile postgres: bundles a Postgres 16 container alongside the app. The app service exposesDATABASE_URL(read from.env) and usesdepends_on: postgres: condition: service_healthy, required: falseso it waits for Postgres to be ready when the profile is active..dockerignore— Excludesnode_modules,.next, Rusttarget/, desktop build artifacts, and local env files./api/health(src/app/api/health/route.ts) — New lightweight endpoint used by the Docker healthcheck, returnsCache-Control: no-storeconsistent with other API routes:GET /api/health → { "status": "ok", "timestamp": "2026-02-24T14:00:00.000Z" }scripts/build-docker.mjs— Post-build script that compilessqlite.ts,sqlite-schema.ts, andsqlite-stores.tsvia esbuild into.next/standalone/.next/server/chunks/db/and copies thebetter-sqlite3native addon into the standalonenode_modules, soROUTA_DB_DRIVER=sqliteworks correctly at runtime in the Docker image.package.json—build:dockerscript runsROUTA_DESKTOP_STANDALONE=1 next buildfollowed bynode scripts/build-docker.mjsto produce a fully functional standalone image.api-contract.yaml— Documents/api/healthalongside all other endpoints.README.md— Docker quickstart instructions added under Quick Start, including a.envexample for enabling the Postgres profile.💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.