From 2dcf2058961d3a803e3574a6bf490ecb0a10fd7b Mon Sep 17 00:00:00 2001 From: Full-Stack Engineer Date: Sun, 15 Mar 2026 18:14:29 +0000 Subject: [PATCH] Add production Dockerfile and docker-compose app service for self-hosted deployment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Multi-stage Dockerfile (deps → build → runner) using oven/bun:1 with standalone Next.js output - App service in docker-compose.yml with postgres healthcheck dependency - .env.docker template with Docker networking defaults (postgres:5432, redis-rest:80) - docker-entrypoint.sh runs prisma migrate deploy on startup - Enable Next.js standalone output in next.config.ts Co-Authored-By: Paperclip --- .dockerignore | 5 ++++ .gitignore | 1 + Dockerfile | 55 +++++++++++++++++++++++++++++++++++++++++ apps/web/.env.docker | 39 +++++++++++++++++++++++++++++ apps/web/next.config.ts | 1 + docker-compose.yml | 20 +++++++++++++++ docker-entrypoint.sh | 11 +++++++++ 7 files changed, 132 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 apps/web/.env.docker create mode 100644 docker-entrypoint.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..270924f2 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +node_modules +.next +.git +*.md +!README.md diff --git a/.gitignore b/.gitignore index 44b9d545..4f0dc6b4 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ yarn-error.log* # env files .env* !.env.example +!.env.docker # vercel .vercel diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..2476ab52 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,55 @@ +# ── Stage 1: Install dependencies ───────────────────────────────── +FROM oven/bun:1 AS deps + +WORKDIR /app + +COPY package.json bun.lock bunfig.toml ./ +COPY apps/web/package.json apps/web/ + +RUN bun install --frozen-lockfile + +# ── Stage 2: Build the Next.js app ─────────────────────────────── +FROM oven/bun:1 AS builder + +WORKDIR /app + +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Generate Prisma client and build +WORKDIR /app/apps/web +RUN bunx prisma generate +RUN bun run build + +WORKDIR /app + +# ── Stage 3: Production runner ─────────────────────────────────── +FROM oven/bun:1-slim AS runner + +WORKDIR /app + +ENV NODE_ENV=production +ENV HOSTNAME="0.0.0.0" +ENV PORT=3000 + +# Copy standalone Next.js output (includes server + minimal node_modules) +COPY --from=builder /app/apps/web/.next/standalone ./ +COPY --from=builder /app/apps/web/.next/static ./apps/web/.next/static +COPY --from=builder /app/apps/web/public ./apps/web/public + +# Copy Prisma schema + migrations for runtime migrate +COPY --from=builder /app/apps/web/prisma ./apps/web/prisma + +# Copy generated Prisma client +COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma +COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma +COPY --from=builder /app/node_modules/prisma ./node_modules/prisma + +# Copy entrypoint script +COPY docker-entrypoint.sh /app/docker-entrypoint.sh +RUN chmod +x /app/docker-entrypoint.sh + +EXPOSE 3000 + +ENTRYPOINT ["/app/docker-entrypoint.sh"] +CMD ["bun", "apps/web/server.js"] diff --git a/apps/web/.env.docker b/apps/web/.env.docker new file mode 100644 index 00000000..8f39016d --- /dev/null +++ b/apps/web/.env.docker @@ -0,0 +1,39 @@ +# ────────────────────────────────────────────── +# Docker Compose environment template +# ────────────────────────────────────────────── +# Copy this file to apps/web/.env before running docker compose up: +# cp apps/web/.env.docker apps/web/.env +# Then fill in the placeholder values below. + +# ────────────────────────────────────────────── +# Database (pre-configured for Docker networking) +# ────────────────────────────────────────────── +DATABASE_URL=postgresql://postgres:postgres@postgres:5432/better_hub + +# ────────────────────────────────────────────── +# Redis (pre-configured for Docker networking) +# ────────────────────────────────────────────── +UPSTASH_REDIS_REST_URL=http://redis-rest:80 +UPSTASH_REDIS_REST_TOKEN=local_token + +# ────────────────────────────────────────────── +# Authentication (required — fill these in) +# ────────────────────────────────────────────── +# Create a GitHub OAuth App: https://github.com/settings/developers +# Set the callback URL to http://localhost:3000/api/auth/callback/github +GITHUB_CLIENT_ID=your_github_oauth_client_id +GITHUB_CLIENT_SECRET=your_github_oauth_client_secret + +# Random 32-char string for session encryption +# Generate with: openssl rand -hex 16 +BETTER_AUTH_SECRET=change_me_to_a_random_secret + +# Base URL of the app +BETTER_AUTH_URL=http://localhost:3000 +NEXT_PUBLIC_APP_URL=http://localhost:3000 + +# ────────────────────────────────────────────── +# AI — Ghost assistant (optional) +# ────────────────────────────────────────────── +# OPEN_ROUTER_API_KEY=your_open_router_api_key +# ANTHROPIC_API_KEY=your_anthropic_api_key diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index 4c17e89c..e55e805b 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -23,6 +23,7 @@ const KNOWN_ROUTES = [ ]; const nextConfig: NextConfig = { + output: "standalone", devIndicators: false, serverExternalPackages: ["@prisma/client"], experimental: { diff --git a/docker-compose.yml b/docker-compose.yml index c20b4bd0..7e8f7c05 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,19 @@ services: + app: + build: + context: . + dockerfile: Dockerfile + container_name: better-hub-app + restart: unless-stopped + ports: + - "3000:3000" + env_file: ./apps/web/.env + depends_on: + postgres: + condition: service_healthy + redis-rest: + condition: service_started + postgres: image: postgres:16-alpine container_name: better-hub-postgres @@ -10,6 +25,11 @@ services: POSTGRES_DB: better_hub ports: - "127.0.0.1:54320:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 5 volumes: - postgres_data:/var/lib/postgresql/data diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 00000000..7706fa92 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -e + +# Run Prisma migrations on startup +echo "Running database migrations..." +cd /app/apps/web +bunx prisma migrate deploy +cd /app + +echo "Starting application..." +exec "$@"