Skip to content

Commit 3be12d3

Browse files
Juan Fernandezclaude
andcommitted
feat: add Docker deployment and CI/CD pipeline
- Multi-stage Dockerfile with standalone Next.js output (~386MB image) - docker-compose.yml with health checks, resource limits, restart policy - GitHub Actions workflow for auto-deploy on push to main - Health check API endpoint at /api/health - .dockerignore for optimized Docker builds Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent dbc4252 commit 3be12d3

6 files changed

Lines changed: 109 additions & 1 deletion

File tree

.dockerignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.git
2+
.github
3+
.vscode
4+
node_modules
5+
.next
6+
.env*
7+
docs
8+
*.md

.github/workflows/deploy.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: Deploy to VPS
2+
3+
on:
4+
push:
5+
branches: [main]
6+
7+
concurrency:
8+
group: deploy
9+
cancel-in-progress: false
10+
11+
jobs:
12+
deploy:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Deploy via SSH
16+
uses: appleboy/ssh-action@v1
17+
with:
18+
host: ${{ secrets.VPS_HOST }}
19+
username: ${{ secrets.VPS_USER }}
20+
key: ${{ secrets.VPS_SSH_KEY }}
21+
script: |
22+
cd /opt/apps/7uanf
23+
24+
# Pull latest changes
25+
git fetch origin main
26+
git reset --hard origin/main
27+
28+
# Build and restart container
29+
docker compose build --no-cache
30+
docker compose up -d --force-recreate --remove-orphans
31+
32+
# Wait for health check
33+
echo "Waiting for health check..."
34+
for i in $(seq 1 30); do
35+
if curl -sf http://localhost:3002/api/health > /dev/null 2>&1; then
36+
echo "App is healthy!"
37+
exit 0
38+
fi
39+
sleep 2
40+
done
41+
echo "Health check failed!"
42+
docker compose logs --tail=50
43+
exit 1

Dockerfile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Stage 1: Dependencies
2+
FROM node:20-alpine AS deps
3+
WORKDIR /app
4+
COPY package.json package-lock.json ./
5+
RUN npm ci
6+
7+
# Stage 2: Build
8+
FROM node:20-alpine AS builder
9+
WORKDIR /app
10+
COPY --from=deps /app/node_modules ./node_modules
11+
COPY . .
12+
RUN npm run build
13+
14+
# Stage 3: Production (minimal image)
15+
FROM node:20-alpine AS runner
16+
WORKDIR /app
17+
ENV NODE_ENV=production
18+
ENV NEXT_TELEMETRY_DISABLED=1
19+
20+
RUN addgroup --system --gid 1001 nodejs && \
21+
adduser --system --uid 1001 nextjs
22+
23+
COPY --from=builder /app/public ./public
24+
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
25+
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
26+
27+
USER nextjs
28+
EXPOSE 3000
29+
ENV PORT=3000
30+
ENV HOSTNAME="0.0.0.0"
31+
32+
CMD ["node", "server.js"]

docker-compose.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
services:
2+
web:
3+
build: .
4+
container_name: 7uanf-web
5+
restart: unless-stopped
6+
ports:
7+
- "127.0.0.1:3002:3000"
8+
mem_limit: 512m
9+
cpus: 1.0
10+
healthcheck:
11+
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health"]
12+
interval: 30s
13+
timeout: 5s
14+
retries: 3
15+
start_period: 15s
16+
environment:
17+
- NODE_ENV=production
18+
- NEXT_TELEMETRY_DISABLED=1

next.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { NextConfig } from "next";
22

33
const nextConfig: NextConfig = {
4-
/* config options here */
4+
output: 'standalone',
55
reactCompiler: true,
66
};
77

src/app/api/health/route.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export async function GET() {
2+
return Response.json({
3+
status: 'ok',
4+
timestamp: new Date().toISOString(),
5+
uptime: process.uptime(),
6+
});
7+
}

0 commit comments

Comments
 (0)