- Add daily-verse API endpoint with 7 rotating verses in Romanian and English - Replace static homepage verse with dynamic fetch from API - Ensure consistent daily rotation using day-of-year calculation - Support both ro and en locales for verse content 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
10 KiB
Git-Based Production Deployment Strategy
Overview
This guide covers how to deploy the latest code from your git repository to production, ensuring your local development changes are properly deployed.
Prerequisites
- Git repository (GitHub, GitLab, Bitbucket, or self-hosted)
- Production server with Docker and Git installed
- SSH access to production server
Deployment Strategies
Option 1: Manual Git Pull Deployment (Simple)
Setup on Production Server
# Clone the repository on production server
cd /opt
git clone https://github.com/yourusername/ghidul-biblic.git
cd ghidul-biblic
# Create production environment file
cp .env.example .env.production
# Edit .env.production with production values
Create Deployment Script
Create /opt/ghidul-biblic/deploy.sh:
#!/bin/bash
set -e
echo "🚀 Starting deployment..."
# Navigate to project directory
cd /opt/ghidul-biblic
# Stop current services
echo "⏹️ Stopping current services..."
docker-compose -f docker-compose.prod.simple.yml down
# Pull latest code
echo "📥 Pulling latest code..."
git pull origin main
# Load environment variables
export $(cat .env.production | xargs)
# Build and start services
echo "🔨 Building and starting services..."
docker-compose -f docker-compose.prod.simple.yml up -d --build
# Wait for health check
echo "🏥 Waiting for health check..."
sleep 30
curl -f http://localhost:3010/api/health || {
echo "❌ Health check failed!"
docker-compose -f docker-compose.prod.simple.yml logs app
exit 1
}
echo "✅ Deployment successful!"
echo "🌐 Application is running at http://localhost:3010"
Make it executable:
chmod +x /opt/ghidul-biblic/deploy.sh
Deploy from Local Machine
# Push your changes to git
git add .
git commit -m "Your changes"
git push origin main
# SSH into production and deploy
ssh user@your-server "cd /opt/ghidul-biblic && ./deploy.sh"
Option 2: Webhook-Based Auto Deployment (Recommended)
Setup Webhook Server on Production
Create /opt/ghidul-biblic/webhook-server.js:
const express = require('express');
const { execSync } = require('child_process');
const crypto = require('crypto');
const app = express();
const PORT = 9000;
const SECRET = process.env.WEBHOOK_SECRET || 'your-webhook-secret';
app.use(express.json());
function verifySignature(payload, signature) {
const hmac = crypto.createHmac('sha256', SECRET);
const digest = 'sha256=' + hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}
app.post('/webhook', (req, res) => {
const signature = req.headers['x-hub-signature-256'];
const payload = JSON.stringify(req.body);
if (!verifySignature(payload, signature)) {
return res.status(401).send('Unauthorized');
}
// Only deploy on push to main branch
if (req.body.ref === 'refs/heads/main') {
console.log('🚀 Webhook received, starting deployment...');
try {
execSync('/opt/ghidul-biblic/deploy.sh', {
stdio: 'inherit',
cwd: '/opt/ghidul-biblic'
});
res.status(200).send('Deployment successful');
} catch (error) {
console.error('Deployment failed:', error);
res.status(500).send('Deployment failed');
}
} else {
res.status(200).send('Not main branch, skipping deployment');
}
});
app.listen(PORT, () => {
console.log(`Webhook server running on port ${PORT}`);
});
Setup Webhook Service
Create /etc/systemd/system/ghidul-webhook.service:
[Unit]
Description=Ghidul Biblic Webhook Server
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/ghidul-biblic
ExecStart=/usr/bin/node webhook-server.js
Restart=always
Environment=WEBHOOK_SECRET=your-webhook-secret-here
[Install]
WantedBy=multi-user.target
Enable and start the service:
systemctl enable ghidul-webhook.service
systemctl start ghidul-webhook.service
systemctl status ghidul-webhook.service
Configure Git Repository Webhook
For GitHub:
- Go to your repository → Settings → Webhooks
- Add webhook:
- URL:
http://your-server:9000/webhook - Content type:
application/json - Secret:
your-webhook-secret-here - Events:
Just the push event
- URL:
For GitLab:
- Go to your project → Settings → Webhooks
- Add webhook:
- URL:
http://your-server:9000/webhook - Secret token:
your-webhook-secret-here - Trigger:
Push events
- URL:
Option 3: GitHub Actions CI/CD (Advanced)
Create GitHub Actions Workflow
Create .github/workflows/deploy.yml:
name: Deploy to Production
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to server
uses: appleboy/ssh-action@v0.1.5
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /opt/ghidul-biblic
./deploy.sh
Setup GitHub Secrets
In your GitHub repository, go to Settings → Secrets and variables → Actions:
HOST: Your production server IPUSERNAME: SSH usernameSSH_KEY: Private SSH key content
Option 4: Docker Hub Auto-Build (Professional)
Setup Dockerfile for Production
Ensure your docker/Dockerfile.prod is optimized:
FROM node:20-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
# Copy source code
COPY . .
# Generate Prisma client
RUN npx prisma generate
# Build application
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# Copy built application
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
COPY --from=builder /app/package.json ./package.json
USER nextjs
EXPOSE 3000
ENV PORT 3000
ENV HOSTNAME "0.0.0.0"
CMD ["node", "server.js"]
Create Docker Compose for Auto-Pull
Create docker-compose.prod.autopull.yml:
version: '3.8'
services:
postgres:
image: pgvector/pgvector:pg16
restart: always
environment:
POSTGRES_DB: bible_chat
POSTGRES_USER: bible_admin
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
- ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- bible_network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U bible_admin -d bible_chat"]
interval: 30s
timeout: 10s
retries: 3
app:
image: yourdockerhub/ghidul-biblic:latest
restart: always
ports:
- "3010:3000"
environment:
DATABASE_URL: postgresql://bible_admin:${DB_PASSWORD}@postgres:5432/bible_chat
AZURE_OPENAI_KEY: ${AZURE_OPENAI_KEY}
AZURE_OPENAI_ENDPOINT: ${AZURE_OPENAI_ENDPOINT}
AZURE_OPENAI_DEPLOYMENT: ${AZURE_OPENAI_DEPLOYMENT}
OLLAMA_API_URL: ${OLLAMA_API_URL}
JWT_SECRET: ${JWT_SECRET}
NEXTAUTH_URL: ${NEXTAUTH_URL}
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
NODE_ENV: production
depends_on:
postgres:
condition: service_healthy
networks:
- bible_network
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/api/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --interval 300 --cleanup app
restart: always
networks:
bible_network:
driver: bridge
volumes:
postgres_data:
Recommended Workflow
For Development
-
Local Development:
# Work on your local machine npm run dev # Make changes, test locally -
Commit and Push:
git add . git commit -m "Feature: Add new functionality" git push origin main -
Automatic Deployment:
- Webhook triggers deployment
- Or manual trigger:
ssh user@server "/opt/ghidul-biblic/deploy.sh"
For Production Server Setup
-
Initial Setup:
# Clone repository git clone https://github.com/yourusername/ghidul-biblic.git /opt/ghidul-biblic cd /opt/ghidul-biblic # Setup environment cp .env.example .env.production # Edit .env.production # Make deployment script executable chmod +x deploy.sh # Setup webhook (optional) npm install express # Setup systemd service -
Deploy:
./deploy.sh
Security Considerations
Git Repository
- Use private repositories for production code
- Use deploy keys or personal access tokens
- Never commit sensitive environment variables
Production Server
- Use non-root user for deployment when possible
- Secure webhook endpoints with proper secrets
- Use SSH key authentication
- Keep server updated
Environment Variables
# .env.production should never be in git
echo ".env.production" >> .gitignore
echo ".env.local" >> .gitignore
Monitoring Deployment
Create Health Check Script
Create /opt/ghidul-biblic/health-check.sh:
#!/bin/bash
HEALTH_URL="http://localhost:3010/api/health"
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" $HEALTH_URL)
if [ $RESPONSE -eq 200 ]; then
echo "✅ Application is healthy"
exit 0
else
echo "❌ Application is unhealthy (HTTP $RESPONSE)"
exit 1
fi
Setup Cron for Health Monitoring
# Add to crontab
*/5 * * * * /opt/ghidul-biblic/health-check.sh >> /var/log/ghidul-health.log 2>&1
Troubleshooting
Common Issues
- Git pull fails: Check SSH keys and repository access
- Docker build fails: Check Dockerfile and dependencies
- Health check fails: Check application logs
- Webhook not triggered: Verify webhook URL and secret
Useful Commands
# Check deployment logs
tail -f /var/log/ghidul-deployment.log
# Check application logs
docker-compose -f docker-compose.prod.simple.yml logs -f app
# Manual health check
curl http://localhost:3010/api/health
# Restart services
docker-compose -f docker-compose.prod.simple.yml restart app
This setup ensures your production environment always has the latest code from your git repository while maintaining proper deployment practices.