Merge pull request 'feat: create production Dockerfile (#37)' (#59) from feature/issue-37-production-dockerfile into develop
Some checks failed
Deploy to Coolify / Code Quality (push) Has been cancelled
Deploy to Coolify / Run Tests (push) Has been cancelled
Deploy to Coolify / Deploy to Development (push) Has been cancelled
Deploy to Coolify / Deploy to Production (push) Has been cancelled
Deploy to Coolify / Deploy to Test (push) Has been cancelled
Some checks failed
Deploy to Coolify / Code Quality (push) Has been cancelled
Deploy to Coolify / Run Tests (push) Has been cancelled
Deploy to Coolify / Deploy to Development (push) Has been cancelled
Deploy to Coolify / Deploy to Production (push) Has been cancelled
Deploy to Coolify / Deploy to Test (push) Has been cancelled
✅ Self-review passed
Production Dockerfile ready for deployment.
This commit was merged in pull request #59.
This commit is contained in:
68
.dockerignore
Normal file
68
.dockerignore
Normal file
@@ -0,0 +1,68 @@
|
||||
# Dependencies
|
||||
node_modules
|
||||
app/node_modules
|
||||
supabase/node_modules
|
||||
|
||||
# Build outputs
|
||||
.nuxt
|
||||
app/.nuxt
|
||||
.output
|
||||
app/.output
|
||||
dist
|
||||
app/dist
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Environment files (use docker env vars instead)
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
app/.env
|
||||
app/.env.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
.DS_Store
|
||||
|
||||
# Git
|
||||
.git
|
||||
.gitignore
|
||||
.gitattributes
|
||||
|
||||
# CI/CD
|
||||
.github
|
||||
.gitea
|
||||
|
||||
# Documentation
|
||||
*.md
|
||||
docs/
|
||||
!README.md
|
||||
|
||||
# Tests
|
||||
test/
|
||||
tests/
|
||||
*.spec.js
|
||||
*.spec.ts
|
||||
*.test.js
|
||||
*.test.ts
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
.cache
|
||||
|
||||
# Supabase (handled separately)
|
||||
supabase/
|
||||
59
Dockerfile
Normal file
59
Dockerfile
Normal file
@@ -0,0 +1,59 @@
|
||||
# Pantry Production Dockerfile
|
||||
# Multi-stage build for optimized production image
|
||||
|
||||
# Stage 1: Build the Nuxt application
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY app/package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci --only=production && \
|
||||
npm cache clean --force
|
||||
|
||||
# Copy application source
|
||||
COPY app/ ./
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Production runtime
|
||||
FROM node:20-alpine AS runner
|
||||
|
||||
# Install dumb-init for proper signal handling
|
||||
RUN apk add --no-cache dumb-init
|
||||
|
||||
# Create app user
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S nodejs -u 1001
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy built application from builder
|
||||
COPY --from=builder --chown=nodejs:nodejs /app/.output /app/.output
|
||||
COPY --from=builder --chown=nodejs:nodejs /app/package*.json ./
|
||||
|
||||
# Switch to non-root user
|
||||
USER nodejs
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Set environment variables
|
||||
ENV NODE_ENV=production \
|
||||
HOST=0.0.0.0 \
|
||||
PORT=3000
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
|
||||
CMD node -e "require('http').get('http://localhost:3000/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
|
||||
|
||||
# Use dumb-init to handle signals properly
|
||||
ENTRYPOINT ["dumb-init", "--"]
|
||||
|
||||
# Start the application
|
||||
CMD ["node", ".output/server/index.mjs"]
|
||||
12
app/server/api/health.get.ts
Normal file
12
app/server/api/health.get.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Health check endpoint for container monitoring
|
||||
*
|
||||
* Returns 200 OK if the server is running
|
||||
*/
|
||||
export default defineEventHandler(() => {
|
||||
return {
|
||||
status: 'ok',
|
||||
timestamp: new Date().toISOString(),
|
||||
uptime: process.uptime()
|
||||
}
|
||||
})
|
||||
90
docker/README.md
Normal file
90
docker/README.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Docker Deployment
|
||||
|
||||
## Production Dockerfile
|
||||
|
||||
The production Dockerfile uses a multi-stage build for optimized image size and security.
|
||||
|
||||
### Build the image
|
||||
|
||||
```bash
|
||||
docker build -t pantry:latest -f Dockerfile .
|
||||
```
|
||||
|
||||
### Run the container
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name pantry \
|
||||
-p 3000:3000 \
|
||||
-e NUXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co \
|
||||
-e NUXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key \
|
||||
pantry:latest
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Required:
|
||||
- `NUXT_PUBLIC_SUPABASE_URL` - Your Supabase project URL
|
||||
- `NUXT_PUBLIC_SUPABASE_ANON_KEY` - Your Supabase anon/public key
|
||||
|
||||
Optional:
|
||||
- `PORT` - Port to listen on (default: 3000)
|
||||
- `HOST` - Host to bind to (default: 0.0.0.0)
|
||||
|
||||
### Health Check
|
||||
|
||||
The container includes a health check endpoint at `/api/health`
|
||||
|
||||
```bash
|
||||
curl http://localhost:3000/api/health
|
||||
```
|
||||
|
||||
Expected response:
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"timestamp": "2026-02-25T00:00:00.000Z",
|
||||
"uptime": 123.456
|
||||
}
|
||||
```
|
||||
|
||||
### Image Features
|
||||
|
||||
- **Multi-stage build**: Separate build and runtime stages
|
||||
- **Alpine Linux**: Minimal base image (~50MB base)
|
||||
- **Non-root user**: Runs as unprivileged user (nodejs:1001)
|
||||
- **dumb-init**: Proper signal handling and zombie reaping
|
||||
- **Health checks**: Built-in container health monitoring
|
||||
- **Production-optimized**: Only production dependencies included
|
||||
|
||||
### Image Size
|
||||
|
||||
Approximate sizes:
|
||||
- Base Alpine + Node.js: ~50MB
|
||||
- Dependencies: ~150MB
|
||||
- Built app: ~20MB
|
||||
- **Total**: ~220MB
|
||||
|
||||
### Security
|
||||
|
||||
- Runs as non-root user (nodejs)
|
||||
- No unnecessary packages
|
||||
- Minimal attack surface
|
||||
- Regular security updates via Alpine base
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
View logs:
|
||||
```bash
|
||||
docker logs pantry
|
||||
```
|
||||
|
||||
Interactive shell:
|
||||
```bash
|
||||
docker exec -it pantry sh
|
||||
```
|
||||
|
||||
Check health:
|
||||
```bash
|
||||
docker inspect --format='{{json .State.Health}}' pantry
|
||||
```
|
||||
Reference in New Issue
Block a user