Skip to content

Add Dockerfile and Docker Compose for Next.js deployment - #18

Merged
phodal merged 3 commits into
mainfrom
copilot/validate-docker-service-access
Feb 25, 2026
Merged

Add Dockerfile and Docker Compose for Next.js deployment#18
phodal merged 3 commits into
mainfrom
copilot/validate-docker-service-access

Conversation

Copilot AI commented Feb 24, 2026

Copy link
Copy Markdown
Contributor

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-root nextjs user.

  • docker-compose.yml — Two modes:

    • Default: SQLite only, fully self-contained (docker compose up)
    • --profile postgres: bundles a Postgres 16 container alongside the app. The app service exposes DATABASE_URL (read from .env) and uses depends_on: postgres: condition: service_healthy, required: false so it waits for Postgres to be ready when the profile is active.
  • .dockerignore — Excludes node_modules, .next, Rust target/, desktop build artifacts, and local env files.

  • /api/health (src/app/api/health/route.ts) — New lightweight endpoint used by the Docker healthcheck, returns Cache-Control: no-store consistent with other API routes:

    GET /api/health → { "status": "ok", "timestamp": "2026-02-24T14:00:00.000Z" }
  • scripts/build-docker.mjs — Post-build script that compiles sqlite.ts, sqlite-schema.ts, and sqlite-stores.ts via esbuild into .next/standalone/.next/server/chunks/db/ and copies the better-sqlite3 native addon into the standalone node_modules, so ROUTA_DB_DRIVER=sqlite works correctly at runtime in the Docker image.

  • package.jsonbuild:docker script runs ROUTA_DESKTOP_STANDALONE=1 next build followed by node scripts/build-docker.mjs to produce a fully functional standalone image.

  • api-contract.yaml — Documents /api/health alongside all other endpoints.

  • README.md — Docker quickstart instructions added under Quick Start, including a .env example for enabling the Postgres profile.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@vercel

vercel Bot commented Feb 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
routa-js Canceled Canceled Feb 24, 2026 3:52pm

…dpoint for Next.js Docker deployment

Co-authored-by: phodal <472311+phodal@users.noreply.github.com>
Copilot AI changed the title [WIP] Verify Docker service access for Next.js app Add Dockerfile and Docker Compose for Next.js deployment Feb 24, 2026
@phodal
phodal marked this pull request as ready for review February 24, 2026 15:31
Copilot AI review requested due to automatic review settings February 24, 2026 15:31
@phodal

phodal commented Feb 24, 2026

Copy link
Copy Markdown
Owner

Augment review

@augmentcode

augmentcode Bot commented Feb 24, 2026

Copy link
Copy Markdown
🤖 Augment PR Summary

Summary: Adds first-class Docker support for the Routa.js Next.js app to enable containerized deployment and easier service verification.

Changes:

  • Introduced a multi-stage Dockerfile that builds a Next.js standalone output and runs it as a non-root user
  • Added docker-compose.yml with a default SQLite-only mode and an optional postgres profile bundling Postgres 16
  • Added .dockerignore to reduce build context size and avoid shipping local artifacts/secrets
  • Implemented GET /api/health (src/app/api/health/route.ts) for container health checks
  • Documented the new endpoint in api-contract.yaml
  • Updated README.md with Docker quickstart instructions
  • Added build:docker script to make the standalone build intent explicit

Technical Notes: The image defaults to SQLite via ROUTA_DB_DRIVER/ROUTA_DB_PATH, and the Compose setup can optionally run Postgres alongside the app.

🤖 Was this summary useful? React with 👍 or 👎

@augmentcode augmentcode Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread docker-compose.yml
- "3000:3000"
environment:
# SQLite mode (default) — no external DB required
ROUTA_DB_DRIVER: ${ROUTA_DB_DRIVER:-sqlite}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:35
  • README.md:96

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Comment thread docker-compose.yml
# Uncomment (or set in .env) to switch to Postgres:
# DATABASE_URL: ${DATABASE_URL}
volumes:
- routa_data:/app/data

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 Dockerfile producing a Next.js standalone runner image.
  • Adds docker-compose.yml with default (self-contained) mode and a postgres profile.
  • Introduces GET /api/health endpoint and documents it in api-contract.yaml; adds Docker quickstart docs and a build:docker script.

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.

Comment thread src/app/api/health/route.ts Outdated
export const dynamic = "force-dynamic";

export function GET() {
return NextResponse.json({ status: "ok", timestamp: new Date().toISOString() });

Copilot AI Feb 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
return NextResponse.json({ status: "ok", timestamp: new Date().toISOString() });
return NextResponse.json(
{ status: "ok", timestamp: new Date().toISOString() },
{ headers: { "Cache-Control": "no-store" } },
);

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added Cache-Control: no-store to the health endpoint response in e938237, consistent with the pattern used by /api/sessions and /api/a2a/card.

Comment thread Dockerfile

# 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

Copilot AI Feb 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
RUN npm run build:docker
RUN npm run build:docker && node scripts/build-desktop-bundle.mjs

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread docker-compose.yml
Comment on lines +21 to +27
# 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

Copilot AI Feb 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
# 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

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread docker-compose.yml
Comment on lines +21 to +34
# 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

Copilot AI Feb 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--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.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread README.md Outdated
Comment on lines +95 to +97
# 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:

Copilot AI Feb 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
# 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

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@phodal

phodal commented Feb 24, 2026

Copy link
Copy Markdown
Owner

@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>
@phodal
phodal merged commit 81ff039 into main Feb 25, 2026
5 of 6 checks passed
@phodal
phodal deleted the copilot/validate-docker-service-access branch February 25, 2026 01:44
sxyseo pushed a commit to sxyseo/routa that referenced this pull request May 15, 2026
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants