feat: add deployment guide, backup functionality, and cron jobs for automated backups
This commit is contained in:
410
DEPLOYMENT_GUIDE_TEMPLATE.md
Normal file
410
DEPLOYMENT_GUIDE_TEMPLATE.md
Normal file
@@ -0,0 +1,410 @@
|
||||
# Docker Git Deployment Strategy - Quick Guide
|
||||
|
||||
A proven deployment strategy for Next.js apps (or any Node.js app) on a VPS using Docker with Git integration.
|
||||
|
||||
## Quick Overview
|
||||
|
||||
**Strategy**: Docker containers + Git repo integration + Zero-downtime deployments
|
||||
**Benefits**: Reproducible builds, easy rollbacks, consistent environments
|
||||
**Time to setup**: ~30 minutes
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Create Docker Files
|
||||
|
||||
### `Dockerfile` (Production)
|
||||
```dockerfile
|
||||
FROM node:22.11.0
|
||||
|
||||
# Set timezone (adjust for your region)
|
||||
ENV TZ=Europe/Warsaw
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
# Install git for repo cloning
|
||||
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Support building from Git repo
|
||||
ARG GIT_REPO_URL
|
||||
ARG GIT_BRANCH=main
|
||||
ARG GIT_COMMIT
|
||||
|
||||
# Clone from git OR use local files
|
||||
RUN if [ -n "$GIT_REPO_URL" ]; then \
|
||||
git clone --branch ${GIT_BRANCH} ${GIT_REPO_URL} . && \
|
||||
if [ -n "$GIT_COMMIT" ]; then git checkout ${GIT_COMMIT}; fi; \
|
||||
fi
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# Copy entrypoint script
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
|
||||
EXPOSE 3000
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
```
|
||||
|
||||
### `docker-entrypoint.sh`
|
||||
```bash
|
||||
#!/bin/bash
|
||||
echo "🚀 Starting application..."
|
||||
|
||||
# Create necessary directories
|
||||
mkdir -p /app/data
|
||||
mkdir -p /app/public/uploads
|
||||
|
||||
# Initialize database, create admin, etc.
|
||||
node scripts/init-setup.js
|
||||
|
||||
# Start the app
|
||||
exec npm start
|
||||
```
|
||||
|
||||
### `docker-compose.prod.yml`
|
||||
```yaml
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
- GIT_REPO_URL=${GIT_REPO_URL}
|
||||
- GIT_BRANCH=${GIT_BRANCH:-main}
|
||||
- GIT_COMMIT=${GIT_COMMIT}
|
||||
ports:
|
||||
- "3001:3000" # HOST:CONTAINER
|
||||
volumes:
|
||||
- ./data:/app/data # Persist database
|
||||
- ./uploads:/app/public/uploads # Persist files
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- TZ=Europe/Warsaw
|
||||
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
|
||||
- NEXTAUTH_URL=${NEXTAUTH_URL}
|
||||
- AUTH_TRUST_HOST=true
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Create Deployment Script
|
||||
|
||||
### `deploy.sh` (Linux/Mac)
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
GIT_REPO_URL=${1:-""}
|
||||
GIT_BRANCH=${2:-"main"}
|
||||
GIT_COMMIT=${3:-""}
|
||||
|
||||
# Load environment variables
|
||||
if [ -f .env.production ]; then
|
||||
export $(grep -v '^#' .env.production | xargs)
|
||||
fi
|
||||
|
||||
# Validate critical vars
|
||||
if [ -z "$NEXTAUTH_SECRET" ] || [ -z "$NEXTAUTH_URL" ]; then
|
||||
echo "ERROR: Set NEXTAUTH_SECRET and NEXTAUTH_URL in .env.production"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build from Git or local files
|
||||
if [ -z "$GIT_REPO_URL" ]; then
|
||||
echo "Building from local files..."
|
||||
docker-compose -f docker-compose.prod.yml build
|
||||
else
|
||||
echo "Building from git: $GIT_REPO_URL (branch: $GIT_BRANCH)"
|
||||
GIT_REPO_URL=$GIT_REPO_URL GIT_BRANCH=$GIT_BRANCH GIT_COMMIT=$GIT_COMMIT \
|
||||
docker-compose -f docker-compose.prod.yml build
|
||||
fi
|
||||
|
||||
# Deploy
|
||||
echo "Deploying..."
|
||||
docker-compose -f docker-compose.prod.yml down
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
|
||||
echo "✅ Deployment completed!"
|
||||
echo "Application running at: $NEXTAUTH_URL (port 3001 on host)"
|
||||
```
|
||||
|
||||
Make it executable:
|
||||
```bash
|
||||
chmod +x deploy.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Configure Environment
|
||||
|
||||
### `.env.production`
|
||||
```bash
|
||||
# Generate secret: openssl rand -base64 32
|
||||
NEXTAUTH_SECRET=your-super-long-random-secret-at-least-32-chars
|
||||
|
||||
# Your public URL
|
||||
NEXTAUTH_URL=https://yourdomain.com
|
||||
|
||||
NODE_ENV=production
|
||||
AUTH_TRUST_HOST=true
|
||||
```
|
||||
|
||||
**⚠️ NEVER commit `.env.production` to Git!**
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Setup VPS
|
||||
|
||||
### Initial VPS Setup
|
||||
```bash
|
||||
# SSH to VPS
|
||||
ssh user@your-vps-ip
|
||||
|
||||
# Install Docker & Docker Compose
|
||||
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||
sudo sh get-docker.sh
|
||||
sudo usermod -aG docker $USER
|
||||
sudo apt install docker-compose-plugin
|
||||
|
||||
# Logout and login again for docker group to take effect
|
||||
exit
|
||||
```
|
||||
|
||||
### Setup Project Directory
|
||||
```bash
|
||||
ssh user@your-vps-ip
|
||||
|
||||
# Create project directory
|
||||
mkdir -p ~/app
|
||||
cd ~/app
|
||||
|
||||
# Copy deployment files (from local machine):
|
||||
# scp docker-compose.prod.yml deploy.sh user@vps-ip:~/app/
|
||||
# scp .env.production user@vps-ip:~/app/
|
||||
|
||||
# OR clone entire repo if using local file deployment:
|
||||
git clone https://your-repo-url.git .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Setup Nginx Reverse Proxy
|
||||
|
||||
### Install Nginx
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install nginx certbot python3-certbot-nginx
|
||||
```
|
||||
|
||||
### Configure Nginx
|
||||
Create `/etc/nginx/sites-available/yourapp`:
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name yourdomain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:3001;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Enable site:
|
||||
```bash
|
||||
sudo ln -s /etc/nginx/sites-available/yourapp /etc/nginx/sites-enabled/
|
||||
sudo nginx -t
|
||||
sudo systemctl restart nginx
|
||||
```
|
||||
|
||||
### Setup SSL (HTTPS)
|
||||
```bash
|
||||
sudo certbot --nginx -d yourdomain.com
|
||||
# Follow prompts to get free SSL certificate
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Deploy!
|
||||
|
||||
### Deployment Methods
|
||||
|
||||
**Method 1: From Local Files**
|
||||
```bash
|
||||
cd ~/app
|
||||
git pull origin main # Update code
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
**Method 2: From Git Repository**
|
||||
```bash
|
||||
cd ~/app
|
||||
./deploy.sh https://git.yourserver.com/user/repo.git main
|
||||
```
|
||||
|
||||
**Method 3: Specific Commit**
|
||||
```bash
|
||||
./deploy.sh https://git.yourserver.com/user/repo.git main abc123def
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ongoing Maintenance
|
||||
|
||||
### View Logs
|
||||
```bash
|
||||
docker-compose -f docker-compose.prod.yml logs -f
|
||||
```
|
||||
|
||||
### Restart App
|
||||
```bash
|
||||
docker-compose -f docker-compose.prod.yml restart
|
||||
```
|
||||
|
||||
### Full Rebuild (for Dockerfile changes)
|
||||
```bash
|
||||
docker-compose -f docker-compose.prod.yml down
|
||||
docker-compose -f docker-compose.prod.yml build --no-cache
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
### Check Container Status
|
||||
```bash
|
||||
docker-compose -f docker-compose.prod.yml ps
|
||||
docker-compose -f docker-compose.prod.yml exec app date # Check timezone
|
||||
```
|
||||
|
||||
### Backup Data
|
||||
```bash
|
||||
# Automated daily database backups are scheduled at 2 AM
|
||||
# Backups are stored in ./backups/ directory, keeping last 30
|
||||
# Check backup logs: docker-compose -f docker-compose.prod.yml exec app cat /app/data/backup.log
|
||||
|
||||
# Manual backup (if needed)
|
||||
tar -czf backup-$(date +%Y%m%d).tar.gz data/ uploads/
|
||||
|
||||
# Download backups to local machine
|
||||
scp user@vps-ip:~/app/backups/backup-*.sqlite ./local-backups/
|
||||
```
|
||||
|
||||
### Rollback to Previous Version
|
||||
```bash
|
||||
# If using Git commits
|
||||
./deploy.sh https://git.yourserver.com/user/repo.git main PREVIOUS_COMMIT_HASH
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Container won't start
|
||||
```bash
|
||||
docker-compose -f docker-compose.prod.yml logs app
|
||||
docker-compose -f docker-compose.prod.yml exec app sh # Get shell inside container
|
||||
```
|
||||
|
||||
### Timezone issues
|
||||
- Make sure `TZ` env var is set in docker-compose
|
||||
- Rebuild image: timezone config is baked in during build
|
||||
- Verify: `docker-compose -f docker-compose.prod.yml exec app date`
|
||||
|
||||
### Permission issues with volumes
|
||||
```bash
|
||||
# Fix ownership
|
||||
sudo chown -R $USER:$USER data/ uploads/
|
||||
```
|
||||
|
||||
### Port already in use
|
||||
```bash
|
||||
# Check what's using port 3001
|
||||
sudo netstat -tulpn | grep 3001
|
||||
# Change port in docker-compose.prod.yml if needed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Checklist
|
||||
|
||||
- [ ] Strong `NEXTAUTH_SECRET` generated (min 32 chars)
|
||||
- [ ] `.env.production` has secure permissions: `chmod 600 .env.production`
|
||||
- [ ] Firewall configured: `sudo ufw allow 80,443/tcp`
|
||||
- [ ] SSL certificate installed via Certbot
|
||||
- [ ] Regular security updates: `sudo apt update && sudo apt upgrade`
|
||||
- [ ] Docker images updated periodically
|
||||
- [ ] Database backups automated
|
||||
- [ ] Git credentials NOT stored in environment files
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```bash
|
||||
# Deploy from Git
|
||||
./deploy.sh https://git.server.com/user/repo.git main
|
||||
|
||||
# Deploy from local files
|
||||
git pull && ./deploy.sh
|
||||
|
||||
# View logs
|
||||
docker-compose -f docker-compose.prod.yml logs -f
|
||||
|
||||
# Restart
|
||||
docker-compose -f docker-compose.prod.yml restart
|
||||
|
||||
# Rebuild completely
|
||||
docker-compose -f docker-compose.prod.yml down
|
||||
docker-compose -f docker-compose.prod.yml build --no-cache
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
|
||||
# Backup
|
||||
tar -czf backup-$(date +%Y%m%d).tar.gz data/ uploads/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Advantages of This Strategy
|
||||
|
||||
✅ **Git Integration**: Deploy specific commits, branches, or tags
|
||||
✅ **Reproducible**: Same build every time
|
||||
✅ **Easy Rollbacks**: Just deploy previous commit
|
||||
✅ **Isolated**: Container doesn't pollute host system
|
||||
✅ **Persistent Data**: Volumes survive container rebuilds
|
||||
✅ **Zero-Config Deployment**: Clone and run `./deploy.sh`
|
||||
✅ **Works Offline**: Can build from local files without Git
|
||||
✅ **Auto-Restart**: Container restarts on crash or reboot
|
||||
|
||||
---
|
||||
|
||||
## Notes to Future Self
|
||||
|
||||
1. **Always use volumes** for data persistence (database, uploads)
|
||||
2. **Timezone matters**: Set it in both Dockerfile and docker-compose
|
||||
3. **Rebuild vs Restart**: Dockerfile changes need rebuild, code changes just restart
|
||||
4. **Port mapping**: Be consistent (I use 3001:3000 - HOST:CONTAINER)
|
||||
5. **Environment secrets**: Never commit, always use `.env.production`
|
||||
6. **Nginx**: Don't forget to setup reverse proxy and SSL
|
||||
7. **Git auth**: For private repos, use SSH keys or tokens in URL
|
||||
8. **Test locally first**: Use `docker-compose.yml` with `Dockerfile.dev`
|
||||
9. **Monitor logs**: Set up log rotation if app is chatty
|
||||
10. **Automate backups**: Cron job for daily database/file backups
|
||||
|
||||
---
|
||||
|
||||
**Time to deploy a new app with this strategy: ~20 minutes** ⚡
|
||||
|
||||
Copy these files, adjust for your app (mainly environment variables and init scripts), and you're production-ready!
|
||||
Reference in New Issue
Block a user