Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Dependencies
node_modules
bun.lockb
bun.lock

# Build outputs
dist
Expand All @@ -9,8 +9,7 @@ build

# Environment files
.env
.env.local
.env.*.local
.env.*

# Logs
logs
Expand Down
77 changes: 40 additions & 37 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,55 +1,58 @@
FROM oven/bun:1.3 AS base
WORKDIR /var/www/api
# ─── Stage 1: Dependencies ────────────────────────────────────────────────────
FROM oven/bun:1-alpine AS deps

# -----------------------------
# deps stage - cache dependencies
# -----------------------------
FROM base AS deps
WORKDIR /app

# Copy package manifests
COPY package.json bun.lock* ./
COPY prisma ./prisma

# Install production dependencies only
RUN bun install --frozen-lockfile
RUN bunx prisma generate
# ─── Stage 2: Builder ─────────────────────────────────────────────────────────
FROM oven/bun:1-alpine AS builder

# -----------------------------
# build stage - compile TypeScript to JavaScript
# -----------------------------
FROM deps AS build
WORKDIR /app

# Copy all source files
COPY . .
RUN bun build src/server.ts --target=bun --production --outdir dist

# -----------------------------
# development stage
# -----------------------------
FROM deps AS development
COPY . .
EXPOSE 3000
CMD ["bun", "src/server.ts"]

# -----------------------------
# production stage
# -----------------------------
# Install all deps (including dev) for Prisma client generation
RUN bun install --frozen-lockfile


FROM oven/bun:1-slim AS production
# prisma generate doesn't connect to the DB, but prisma.config.ts uses env("DATABASE_URL")
# which throws at config-load time if the var is absent. Satisfy it with a dummy placeholder.
# The real DATABASE_URL is injected at runtime via env_file / --env-file / -e flags.
ARG DATABASE_URL=postgresql://placeholder:placeholder@localhost:5432/placeholder
ENV DATABASE_URL=${DATABASE_URL}

WORKDIR /var/www/api
RUN groupadd -g 1001 nodejs && useradd -u 1001 -g nodejs -m bunjs
# Generate Prisma client
RUN bunx prisma generate

COPY --from=build --chown=bunjs:nodejs /var/www/api/dist ./dist
COPY --from=deps --chown=bunjs:nodejs /var/www/api/src/generated ./dist/generated
# Unset the dummy URL so it doesn't leak into downstream stages via ENV
ENV DATABASE_URL=

# ─── Stage 3: Production runner
FROM oven/bun:1-alpine AS runner

RUN chown -R bunjs:nodejs /var/www/api
USER bunjs
WORKDIR /app

ENV NODE_ENV=production
ENV PORT=3000

EXPOSE 3000
# Create a non-root user for security
RUN addgroup --system --gid 1001 cocgroup && \
adduser --system --uid 1001 cocuser

# Copy production node_modules from deps stage
COPY --from=deps --chown=cocuser:cocgroup /app/node_modules ./node_modules

# Copy application source
COPY --chown=cocuser:cocgroup . .

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
COPY --from=builder --chown=cocuser:cocgroup /app/src/generated/prisma ./src/generated/prisma

USER cocuser

EXPOSE 3000

CMD ["bun", "./dist/server.js"]
# Run Prisma migrations then start the server
CMD ["sh", "-c", "bunx prisma migrate deploy && bun src/server.ts"]
54 changes: 0 additions & 54 deletions LOCAL_DEVELOPMENT.md

This file was deleted.

78 changes: 35 additions & 43 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,45 +1,37 @@
services:
api:
image: coc-api
build:
context: .
dockerfile: Dockerfile
target: development
ports:
- "127.0.0.1:3000:3000"
env_file:
- .env
environment:
NODE_ENV: development
PORT: 3000
DATABASE_URL: "postgresql://postgres:example@db:5432/coc?sslmode=disable"
volumes:
- ./:/app:cached
- /app/node_modules
depends_on:
- db
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:3000/health"]
interval: 30s
timeout: 3s
retries: 3
name: coc-api

db:
image: postgres:17
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: example
POSTGRES_DB: coc
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "127.0.0.1:5432:5432"
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres", "-d", "coc", "-h", "127.0.0.1", "-p", "5432"]
interval: 5s
timeout: 5s
retries: 5
services:
api:
build:
context: .
dockerfile: Dockerfile
target: builder # Use builder stage in dev (has devDeps + bun watch)
container_name: coc-api-dev
restart: unless-stopped
ports:
- "${PORT:-3000}:3000"
env_file:
- .env.local # Local dev env vars (Supabase connection strings, etc.)
environment:
NODE_ENV: development
volumes:
# Mount source for live-reload (everything except node_modules)
- .:/app
- /app/node_modules
- /app/node_modules/.prisma
command: >
sh -c "bunx prisma generate &&
bunx prisma migrate deploy &&
bun --watch src/server.ts"
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
networks:
- coc-network

volumes:
pgdata:
seed-data:
networks:
coc-network:
driver: bridge
Loading
Loading