Skip to main content

Overview

Docker deployment provides the most complete Routa experience with:
  • Full agent support (persistent processes for ACP agents)
  • Choice of database (SQLite or PostgreSQL)
  • Isolated environment (no dependency conflicts)
  • Easy scaling (compose profiles for different configurations)

Quick Start

SQLite (Default)

No external database required - perfect for single-user deployments.
# Clone the repository
git clone https://github.com/routa/routa-js.git
cd routa-js

# Build and start
docker compose up --build
Access Routa at http://localhost:3000 Health check: http://localhost:3000/api/health Use the bundled PostgreSQL container for multi-user deployments.
1

Create .env file

.env
ROUTA_DB_DRIVER=postgres
DATABASE_URL=postgresql://routa:routa_secret@postgres:5432/routa
POSTGRES_PASSWORD=routa_secret
2

Start with Postgres profile

docker compose --profile postgres up --build

Docker Compose Configuration

The project includes a production-ready docker-compose.yml:
docker-compose.yml
services:
  # ── Next.js application ──
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      # SQLite mode (default) — no external DB required
      ROUTA_DB_DRIVER: ${ROUTA_DB_DRIVER:-sqlite}
      ROUTA_DB_PATH: /app/data/routa.db
      # Set DATABASE_URL (and ROUTA_DB_DRIVER=postgres) in .env to use Postgres.
      DATABASE_URL: ${DATABASE_URL:-}
    volumes:
      - routa_data:/app/data
    depends_on:
      postgres:
        condition: service_healthy
        required: false
    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

  # ── PostgreSQL (optional) ──
  postgres:
    image: postgres:16-alpine
    profiles:
      - postgres
    environment:
      POSTGRES_DB: routa
      POSTGRES_USER: routa
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-routa_secret}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U routa -d routa"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  routa_data:
  postgres_data:

Dockerfile Explanation

The multi-stage Dockerfile optimizes image size and build time:
Dockerfile
# Stage 1: Install dependencies
FROM node:22-alpine AS deps
RUN apk add --no-cache libc6-compat python3 make g++
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --legacy-peer-deps

# Stage 2: Build application
FROM node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build:docker

# Stage 3: Production runner
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV ROUTA_DB_DRIVER=sqlite
ENV ROUTA_DB_PATH=/app/data/routa.db

RUN addgroup --system --gid 1001 nodejs && \
    adduser  --system --uid 1001 nextjs

COPY --from=builder /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder /app/public ./public

RUN mkdir -p /app/data && chown nextjs:nodejs /app/data

USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME=0.0.0.0

CMD ["node", "server.js"]
Key features:
  • Uses standalone output mode (includes only necessary files)
  • Alpine Linux base (smaller image size)
  • Non-root user for security
  • Persistent data volume at /app/data

Environment Configuration

SQLite Configuration

.env
# Database
ROUTA_DB_DRIVER=sqlite
ROUTA_DB_PATH=/app/data/routa.db

# Anthropic API
ANTHROPIC_AUTH_TOKEN=sk-ant-...

# Optional: Model configuration
ANTHROPIC_MODEL=claude-4-sonnet-20250514
ANTHROPIC_SMALL_FAST_MODEL=claude-4-haiku-20250514

PostgreSQL Configuration

.env
# Database
ROUTA_DB_DRIVER=postgres
DATABASE_URL=postgresql://routa:routa_secret@postgres:5432/routa
POSTGRES_PASSWORD=routa_secret

# Anthropic API
ANTHROPIC_AUTH_TOKEN=sk-ant-...

# Optional: Custom endpoint
# ANTHROPIC_BASE_URL=https://api.anthropic.com

# Optional: Request timeout
# API_TIMEOUT_MS=600000

# Optional: GitHub integration
# GITHUB_TOKEN=ghp_...
# GITHUB_WEBHOOK_SECRET=your-secret

External PostgreSQL

To use an external PostgreSQL database (e.g., Neon, Supabase):
.env
ROUTA_DB_DRIVER=postgres
DATABASE_URL=postgresql://user:password@external-host:5432/routa?sslmode=require

# Don't start the bundled postgres service
# Use: docker compose up app

Docker Compose Profiles

Default Profile (SQLite)

# Start only the app service with SQLite
docker compose up

Postgres Profile

# Start app + postgres services
docker compose --profile postgres up

Custom Profiles

Add custom services to docker-compose.yml:
docker-compose.yml
services:
  redis:
    image: redis:7-alpine
    profiles:
      - cache
    ports:
      - "6379:6379"

  nginx:
    image: nginx:alpine
    profiles:
      - production
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
Start with multiple profiles:
docker compose --profile postgres --profile cache up

Volume Management

Backup SQLite Database

# Export volume to tar
docker run --rm \
  -v routa-js_routa_data:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/routa-data-backup.tar.gz -C /data .

Restore SQLite Database

# Import tar to volume
docker run --rm \
  -v routa-js_routa_data:/data \
  -v $(pwd):/backup \
  alpine tar xzf /backup/routa-data-backup.tar.gz -C /data

Backup PostgreSQL

# Dump database
docker compose exec postgres pg_dump -U routa routa > routa-backup.sql

# Restore database
docker compose exec -T postgres psql -U routa routa < routa-backup.sql

Inspect Volumes

# List volumes
docker volume ls

# Inspect volume
docker volume inspect routa-js_routa_data

# Access volume filesystem
docker run --rm -it \
  -v routa-js_routa_data:/data \
  alpine sh

Production Deployment

Using Docker Compose in Production

# Build images
docker compose build

# Start services in detached mode
docker compose --profile postgres up -d

# View logs
docker compose logs -f app

# Stop services
docker compose down

# Stop and remove volumes (WARNING: deletes data)
docker compose down -v

Environment-specific Compose Files

Create docker-compose.prod.yml:
docker-compose.prod.yml
services:
  app:
    restart: always
    environment:
      NODE_ENV: production
      LOG_LEVEL: info
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 4G
        reservations:
          cpus: '1'
          memory: 2G

  postgres:
    restart: always
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 2G
Use with:
docker compose -f docker-compose.yml -f docker-compose.prod.yml --profile postgres up -d

Reverse Proxy with Nginx

nginx.conf
upstream routa_backend {
    server localhost:3000;
}

server {
    listen 80;
    server_name routa.yourdomain.com;

    location / {
        proxy_pass http://routa_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }

    location /api/mcp {
        proxy_pass http://routa_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }
}
Add to docker-compose.yml:
services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app

Monitoring and Logs

View Logs

# Follow logs for all services
docker compose logs -f

# Follow logs for specific service
docker compose logs -f app

# Last 100 lines
docker compose logs --tail=100 app

# Logs since timestamp
docker compose logs --since 2026-03-03T12:00:00 app

Health Checks

# Check container health
docker compose ps

# Inspect health status
docker inspect --format='{{json .State.Health}}' routa-js-app-1 | jq

# Manual health check
curl http://localhost:3000/api/health

Resource Usage

# Live stats
docker stats

# Container resource usage
docker compose ps -q | xargs docker stats --no-stream

Troubleshooting

Check logs for errors:
docker compose logs app
Common issues:
  • Missing environment variables
  • Database connection failure
  • Port already in use
SQLite:
  • Ensure volume is mounted: -v routa_data:/app/data
  • Check file permissions: ls -la inside container
PostgreSQL:
  • Wait for postgres health check: docker compose ps
  • Verify DATABASE_URL matches postgres service name
  • Check postgres logs: docker compose logs postgres
Change ports in docker-compose.yml:
ports:
  - "3001:3000"  # Host:Container
Clear Docker build cache:
docker compose build --no-cache
Fix volume ownership:
docker compose exec app chown -R nextjs:nodejs /app/data

Advanced Configuration

Custom Build Args

Modify docker-compose.yml:
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        NODE_VERSION: 22
        BUILD_DATE: ${BUILD_DATE}

Multi-Platform Builds

# Build for multiple architectures
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t routa:latest \
  .

Development with Hot Reload

Create docker-compose.dev.yml:
docker-compose.dev.yml
services:
  app:
    build:
      target: deps
    command: npm run dev
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      NODE_ENV: development
Run with:
docker compose -f docker-compose.yml -f docker-compose.dev.yml up

Next Steps

Custom Specialists

Define custom agent roles

Custom MCP Servers

Integrate external MCP tools

Workflows

Automate multi-step processes

GitHub Integration

Import GitHub repositories

Build docs developers (and LLMs) love