Rework deployment.

This commit is contained in:
David Uhrin 2025-10-22 16:17:16 +02:00
parent 831c3716e4
commit 3c424fc325
9 changed files with 514 additions and 687 deletions

View file

@ -1,84 +1,127 @@
# Hybrid Deployment Guide: Apps in Image + Persistent Volumes
# Zerops Deployment Guide: Managed Services + Docker Compose
This configuration implements the best of both worlds:
- **Apps baked into Docker image** for consistency and fast deployments
- **Site data in persistent volumes** for data safety and persistence
This configuration leverages Zerops' managed services for databases while using Docker Compose for application containers, providing the best of both worlds:
- **Managed MariaDB and Redis** for reliability, backups, and performance
- **Docker Compose applications** for flexibility and familiar workflows
- **Automated site installation** with custom apps included
## Architecture Overview
### What's in the Image (Built once, reusable):
- ✅ Frappe and ERPNext applications
- ✅ All Python dependencies
- ✅ Custom apps (if configured)
- ✅ Basic directory structure
### Zerops Managed Services:
- ✅ **MariaDB 11** - Database service with automatic backups and monitoring
- ✅ **Valkey 7.2 (Redis)** - Cache service for high-performance caching
- ✅ **Valkey 7.2 (Redis)** - Queue service for background job processing
### What's in Persistent Volumes:
- ✅ Site configurations (`sites/[site-name]/site_config.json`)
- ✅ Uploaded files (`sites/[site-name]/public/files/`)
- ✅ Private files (`sites/[site-name]/private/files/`)
- ✅ Site-specific customizations
- ✅ Database backups
### Docker Compose Application Services:
- ✅ **Backend** - Main Frappe/ERPNext application server
- ✅ **Frontend** - Nginx reverse proxy for web serving
- ✅ **WebSocket** - Real-time communication service
- ✅ **Queue Workers** - Background job processing (short & long tasks)
- ✅ **Scheduler** - Cron job management
### Automated Site Setup:
- ✅ Site creation and configuration
- ✅ ERPNext application installation
- ✅ Custom XML Importer app installation
- ✅ Database migrations and setup
- ✅ Application logs
## Deployment Steps
### 1. Set Required Environment Secrets in Zerops
### 1. Create Zerops Services
```bash
# In Zerops Dashboard > Project > Environment Variables
db_password=YourSecureDbPassword123
admin_password=YourAdminPassword123
site_name=your-domain.com
Create these services in your Zerops project:
```yaml
# Database service
- Service Name: db
- Type: MariaDB 11
- Mode: Default (or HA for production)
# Redis Cache service
- Service Name: redis-cache
- Type: Valkey 7.2
- Mode: NON_HA (or HA for production)
# Redis Queue service
- Service Name: redis-queue
- Type: Valkey 7.2
- Mode: NON_HA (or HA for production)
```
### 2. Deploy the Configuration
### 2. Set Environment Secrets
1. Import the `zerops.yml` file to Zerops
2. Zerops will build all services using `Dockerfile.zerops`
3. Each service will run the initialization script on startup
4. Site will be automatically created if it doesn't exist
In Zerops Dashboard > Project > Environment Variables:
### 3. Automatic Initialization Process
```bash
dbPassword=YourSecureDbPassword123
adminPassword=YourAdminPassword123
siteName=your-domain.com
```
On first deployment, each container will:
### 3. Deploy the Application
1. **Build Phase** (runs once during image creation):
- Install Frappe/ERPNext apps into image
- Copy initialization scripts
- Set proper permissions
1. Connect your GitHub repository to Zerops
2. Set branch to `zerops`
3. Zerops will automatically find and deploy using `zerops.yml`
4. The deployment will:
- Create managed database and Redis services
- Pull and start Docker Compose services
- Run site installation script
- Configure all service connections
### 4. Automatic Site Installation
During deployment, the installation script will:
1. **Service Verification**:
- Wait for database connection to be ready
- Verify Redis cache and queue services are available
- Pull required Docker images
2. **Site Setup**:
- Check if site already exists
- Create new site if needed (with database and admin user)
- Install ERPNext application
- Install custom XML Importer app from GitHub
- Run database migrations
2. **Runtime Phase** (runs on every container start):
- **Backend service**: Full site initialization (creates site, installs ERPNext)
- **Other services**: Light initialization (just checks if site exists)
- Configure database and Redis connections
- Wait for database to be ready (backend only)
- Set proper permissions and configurations
3. **Service Startup**:
- Start all Docker Compose services
- Services connect to managed databases using service names
- Site becomes available at the app service URL
## Benefits of This Approach
## Benefits of This Architecture
### ✅ **Container Restart Resilience**
- Site data survives container restarts/crashes
### ✅ **Managed Database Reliability**
- Automatic backups and point-in-time recovery
- Built-in monitoring and alerting
- High availability options
- No database container overhead
### ✅ **Docker Compose Flexibility**
- Familiar development workflow
- Easy service management and scaling
- Shared volumes for site data
- Service dependencies handled automatically
### ✅ **Automated Setup**
- Zero-touch deployment
- Custom apps automatically installed
- Site creation and configuration handled
- No manual intervention required
- Fast recovery times
### ✅ **Consistent App Deployments**
- Apps are identical across all containers
- No version drift between services
- Easy to update apps (rebuild image)
### ✅ **Service Discovery**
- Internal service communication via names
- No hardcoded IPs or complex networking
- Zerops handles internal DNS resolution
### ✅ **Zero-Downtime Updates**
- App updates: rebuild image, rolling update
- Site data preserved during updates
- Database migrations handled automatically
### ✅ **Backup & Recovery**
- Simple volume snapshots for site data
- Database backups work seamlessly
- Easy disaster recovery
### ✅ **Scalability**
- New containers start with same apps
- Shared persistent volume for site data
### ✅ **Scalability & Performance**
- Database services can be scaled independently
- Application services can be horizontally scaled
- Redis services optimized for their use case
- No database performance impact from containerization
- Auto-scaling works out of the box
## File Structure
@ -98,51 +141,51 @@ On first deployment, each container will:
└── logs/ # Application logs (persistent volume)
```
## Access Your ERPNext Instance
After successful deployment:
1. **Find your app service URL** in Zerops Dashboard > Services > app
2. **Access ERPNext** at `https://your-app-service-url`
3. **Login credentials**:
- Username: `Administrator`
- Password: `[value you set for adminPassword]`
## Common Operations
### Adding New Custom Apps
1. **Update `Dockerfile.zerops`**:
```dockerfile
RUN bench get-app --branch main custom_app https://github.com/user/custom_app.git
```
2. **Redeploy services** (Zerops will rebuild image)
3. **Install app on site** (automatic or manual):
1. **Update the installation script** `scripts/install-site.sh`:
```bash
bench --site your-domain.com install-app custom_app
# Add your custom app
bench get-app https://github.com/user/custom_app.git
bench --site "$FRAPPE_SITE_NAME_HEADER" install-app custom_app
```
2. **Redeploy the application** - Zerops will run the updated script
### Updating Frappe/ERPNext Version
1. **Update version in `Dockerfile.zerops`**:
```dockerfile
FROM frappe/erpnext:v16.0.0
```
2. **Update `zerops.yml` environment**:
1. **Update version in `zerops.yml`**:
```yaml
CUSTOM_TAG: v16.0.0
ERPNEXT_VERSION: v16.0.0
```
3. **Redeploy** - migrations run automatically
2. **Redeploy** - migrations run automatically during site setup
### Manual Site Operations
Access any service terminal in Zerops dashboard:
Access the app service terminal in Zerops dashboard:
```bash
# Navigate to bench directory
# Navigate to bench directory
cd /home/frappe/frappe-bench
# Run migrations
bench --site your-domain.com migrate
# Create new site
bench new-site newsite.com --install-app erpnext
# Install custom app
# Install additional apps
bench --site your-domain.com install-app custom_app
# Backup site
@ -154,7 +197,7 @@ bench --site your-domain.com console
## Troubleshooting
### Site Not Created Automatically
### Site Installation Issues
- Check environment variables are set correctly
- Check database connectivity
- Review container logs in Zerops dashboard

116
docker-compose.zerops.yaml Normal file
View file

@ -0,0 +1,116 @@
# Docker Compose configuration for Zerops deployment
# This extends the main compose.yaml with Zerops-specific requirements
x-customizable-image: &customizable_image
# Use ERPNext image with custom apps from our Containerfile
image: ${CUSTOM_IMAGE:-frappe/erpnext}:${CUSTOM_TAG:-v15.84.0}
pull_policy: always
restart: unless-stopped
# IMPORTANT: All services must use network_mode: host for Zerops
network_mode: host
x-depends-on-configurator: &depends_on_configurator
depends_on:
configurator:
condition: service_completed_successfully
x-backend-defaults: &backend_defaults
<<: [*depends_on_configurator, *customizable_image]
volumes:
- sites:/home/frappe/frappe-bench/sites
environment:
- DB_HOST
- DB_PORT
- DB_PASSWORD
- ADMIN_PASSWORD
- REDIS_CACHE
- REDIS_QUEUE
- SOCKETIO_PORT
- FRAPPE_SITE_NAME_HEADER
services:
configurator:
<<: *backend_defaults
platform: linux/amd64
entrypoint:
- bash
- -c
# Basic Frappe configuration only (site installation handled by Zerops)
command:
- >
ls -1 apps > sites/apps.txt;
bench set-config -g db_host $$DB_HOST;
bench set-config -gp db_port $$DB_PORT;
bench set-config -g redis_cache "redis://$$REDIS_CACHE";
bench set-config -g redis_queue "redis://$$REDIS_QUEUE";
bench set-config -g redis_socketio "redis://$$REDIS_QUEUE";
bench set-config -gp socketio_port $$SOCKETIO_PORT;
echo "✅ Frappe configuration completed";
environment:
DB_HOST: ${DB_HOST}
DB_PORT: ${DB_PORT:-3306}
DB_PASSWORD: ${DB_PASSWORD}
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
REDIS_CACHE: ${REDIS_CACHE}
REDIS_QUEUE: ${REDIS_QUEUE}
SOCKETIO_PORT: 9000
FRAPPE_SITE_NAME_HEADER: ${FRAPPE_SITE_NAME_HEADER}
depends_on: {}
restart: on-failure
backend:
<<: *backend_defaults
platform: linux/amd64
ports:
- "8000:8000"
frontend:
<<: *customizable_image
platform: linux/amd64
command:
- nginx-entrypoint.sh
environment:
- BACKEND=127.0.0.1:8000
- SOCKETIO=127.0.0.1:9000
- FRAPPE_SITE_NAME_HEADER
- UPSTREAM_REAL_IP_ADDRESS=127.0.0.1
- UPSTREAM_REAL_IP_HEADER=X-Forwarded-For
- UPSTREAM_REAL_IP_RECURSIVE=off
- PROXY_READ_TIMEOUT=120
- CLIENT_MAX_BODY_SIZE=50m
volumes:
- sites:/home/frappe/frappe-bench/sites
depends_on:
- backend
- websocket
ports:
- "8080:8080"
websocket:
<<: [*depends_on_configurator, *customizable_image]
platform: linux/amd64
command:
- node
- /home/frappe/frappe-bench/apps/frappe/socketio.js
volumes:
- sites:/home/frappe/frappe-bench/sites
ports:
- "9000:9000"
queue-short:
<<: *backend_defaults
platform: linux/amd64
command: bench worker --queue short,default
queue-long:
<<: *backend_defaults
platform: linux/amd64
command: bench worker --queue long,default,short
scheduler:
<<: *backend_defaults
platform: linux/amd64
command: bench schedule
volumes:
sites:

View file

@ -1,81 +0,0 @@
#!/bin/bash
# Frappe Site Initialization Script for Production Deployment
# This script runs before the main service starts to ensure the site exists and is configured
set -e
echo "🔧 Initializing Frappe site..."
# Configuration from environment variables
SITE_NAME=${FRAPPE_SITE_NAME_HEADER:-"localhost"}
DB_PASSWORD=${DB_PASSWORD:-"admin"}
ADMIN_PASSWORD=${ADMIN_PASSWORD:-"admin"}
DB_HOST=${DB_HOST:-"db"}
DB_PORT=${DB_PORT:-"3306"}
echo "📋 Configuration: Site Name: $SITE_NAME, Database Host: $DB_HOST:$DB_PORT"
# Navigate to frappe bench directory
cd /home/frappe/frappe-bench
# Configure database and Redis connections
echo "🔗 Configuring connections..."
bench set-config -g db_host "$DB_HOST" || echo "DB host config already set"
bench set-config -gp db_port "$DB_PORT" || echo "DB port config already set"
bench set-config -g redis_cache "redis://${REDIS_CACHE}:6379" || echo "Redis cache config already set"
bench set-config -g redis_queue "redis://${REDIS_QUEUE}:6379" || echo "Redis queue config already set"
bench set-config -g redis_socketio "redis://${REDIS_QUEUE}:6379" || echo "Redis socketio config already set"
bench set-config -gp socketio_port "${SOCKETIO_PORT}" || echo "Socketio port config already set"
# Wait for database to be ready
echo "⏳ Waiting for database to be ready..."
for i in {1..30}; do
if mysql -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_PASSWORD" -e "SELECT 1;" >/dev/null 2>&1; then
echo "✅ Database is ready!"
break
fi
echo " Attempt $i/30: Database not ready, waiting 5 seconds..."
sleep 5
done
# Check if site exists in persistent volume
if [ -d "sites/$SITE_NAME" ]; then
echo "✅ Site '$SITE_NAME' already exists, updating configuration"
bench --site "$SITE_NAME" set-config db_host "$DB_HOST"
bench --site "$SITE_NAME" set-config db_port "$DB_PORT"
bench use "$SITE_NAME" || echo "Site already set as default"
else
echo "🏗️ Creating new site: $SITE_NAME"
if bench new-site "$SITE_NAME" \
--db-root-password "$DB_PASSWORD" \
--admin-password "$ADMIN_PASSWORD" \
--install-app erpnext \
--set-default; then
echo "✅ Site created successfully!"
# Install custom XML importer app
echo "📦 Installing XML Importer app..."
bench get-app --branch main erpnext_xml_importer https://github.com/UhrinDavid/erpnext_xml_importer.git || echo "⚠️ Failed to download XML importer"
bench --site "$SITE_NAME" install-app erpnext_xml_importer || echo "⚠️ XML importer app installation failed"
bench --site "$SITE_NAME" set-config developer_mode 0
bench --site "$SITE_NAME" set-config maintenance_mode 0
echo "🎉 Site '$SITE_NAME' is ready with XML Importer!"
else
echo "❌ Failed to create site. Checking if it exists..."
if [ -d "sites/$SITE_NAME" ]; then
echo "⚠️ Site directory exists but creation failed. Using existing site."
bench use "$SITE_NAME" || echo "Could not set as default"
else
echo "💥 Site creation failed completely. Exiting."
exit 1
fi
fi
fi
# Ensure proper ownership of sites directory
chown -R frappe:frappe sites/ || echo "Could not change ownership"
chmod -R 755 sites/ || echo "Could not change permissions"
echo "✅ Site initialization completed successfully!"

View file

@ -1,20 +0,0 @@
#!/bin/bash
# Simple service initialization for Frappe services
# This script runs on services that don't need to create sites
set -e
SERVICE_NAME=${1:-"frappe-service"}
echo "🔧 Initializing $SERVICE_NAME..."
cd /home/frappe/frappe-bench
# Basic configuration (site creation handled by backend service)
if [ -f sites/common_site_config.json ] && [ -n "${FRAPPE_SITE_NAME_HEADER}" ]; then
if [ -d "sites/${FRAPPE_SITE_NAME_HEADER}" ]; then
bench use "${FRAPPE_SITE_NAME_HEADER}" || echo "Site will be set by backend"
fi
fi
echo "$SERVICE_NAME ready"

View file

@ -1,99 +0,0 @@
#!/bin/bash
# Frappe Site Initialization Script for Zerops
# This script runs on container start to ensure the site exists
set -e
echo "🔧 Initializing Frappe site..."
# Configuration from environment variables
SITE_NAME=${FRAPPE_SITE_NAME_HEADER:-"localhost"}
DB_PASSWORD=${DB_PASSWORD:-"admin"}
ADMIN_PASSWORD=${ADMIN_PASSWORD:-"admin"}
DB_HOST=${DB_HOST:-"db"}
DB_PORT=${DB_PORT:-"3306"}
echo "📋 Configuration:"
echo " Site Name: $SITE_NAME"
echo " Database Host: $DB_HOST:$DB_PORT"
# Navigate to frappe bench directory
cd /home/frappe/frappe-bench
# Ensure sites directory exists and has proper permissions
mkdir -p sites
chown -R frappe:frappe sites || true
# Configure database and Redis connections
echo "🔗 Configuring connections..."
bench set-config -g db_host "$DB_HOST" || echo "DB host config already set"
bench set-config -gp db_port "$DB_PORT" || echo "DB port config already set"
bench set-config -g redis_cache "redis://${REDIS_CACHE}:6379" || echo "Redis cache config already set"
bench set-config -g redis_queue "redis://${REDIS_QUEUE}:6379" || echo "Redis queue config already set"
bench set-config -g redis_socketio "redis://${REDIS_QUEUE}:6379" || echo "Redis socketio config already set"
bench set-config -gp socketio_port "${SOCKETIO_PORT}" || echo "Socketio port config already set"
# Wait for database to be ready
echo "⏳ Waiting for database to be ready..."
for i in {1..30}; do
if mysql -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_PASSWORD" -e "SELECT 1;" >/dev/null 2>&1; then
echo "✅ Database is ready!"
break
fi
echo " Attempt $i/30: Database not ready, waiting 5 seconds..."
sleep 5
done
# Check if site already exists in persistent volume
if [ -d "sites/$SITE_NAME" ]; then
echo "✅ Site '$SITE_NAME' already exists, skipping creation"
# Ensure site is in the current bench context
if [ -f "sites/$SITE_NAME/site_config.json" ]; then
echo "🔧 Updating site configuration..."
bench --site "$SITE_NAME" set-config db_host "$DB_HOST"
bench --site "$SITE_NAME" set-config db_port "$DB_PORT"
bench use "$SITE_NAME" || echo "Site already set as default"
fi
else
echo "🏗️ Creating new site: $SITE_NAME"
# Create the site with ERPNext
if bench new-site "$SITE_NAME" \
--db-root-password "$DB_PASSWORD" \
--admin-password "$ADMIN_PASSWORD" \
--install-app erpnext \
--set-default; then
echo "✅ Site created successfully!"
# Additional site configuration
bench --site "$SITE_NAME" set-config developer_mode 0
bench --site "$SITE_NAME" set-config maintenance_mode 0
echo "🎉 Site '$SITE_NAME' is ready!"
else
echo "❌ Failed to create site. Checking if it exists..."
if [ -d "sites/$SITE_NAME" ]; then
echo "⚠️ Site directory exists but creation failed. Using existing site."
bench use "$SITE_NAME" || echo "Could not set as default"
else
echo "💥 Site creation failed completely. Exiting."
exit 1
fi
fi
fi
# Ensure proper ownership of sites directory
chown -R frappe:frappe sites/ || echo "Could not change ownership"
chmod -R 755 sites/ || echo "Could not change permissions"
# Final verification
if [ -d "sites/$SITE_NAME" ]; then
echo "✅ Site initialization completed successfully!"
echo "🌐 Site '$SITE_NAME' is ready to serve traffic"
else
echo "❌ Site initialization failed!"
exit 1
fi

84
scripts/install-site.sh Normal file
View file

@ -0,0 +1,84 @@
#!/bin/bash
# Frappe/ERPNext Site Installation Script
# This script creates and configures a new Frappe site with ERPNext and custom apps
# Runs during Zerops deployment before starting application services
set -e
echo "🚀 Starting Frappe site installation..."
# Configuration from environment variables
SITE_NAME=${FRAPPE_SITE_NAME_HEADER:-"localhost"}
DB_PASSWORD=${DB_PASSWORD:-"admin"}
ADMIN_PASSWORD=${ADMIN_PASSWORD:-"admin"}
DB_HOST=${DB_HOST:-"db"}
DB_PORT=${DB_PORT:-"3306"}
echo "📋 Site Configuration:"
echo " - Site Name: $SITE_NAME"
echo " - Database Host: $DB_HOST:$DB_PORT"
echo " - Admin Password: [CONFIGURED]"
# Start with a fresh container to install the site
echo "📦 Starting temporary Frappe container for site installation..."
# Run the installation inside a Docker container
docker compose -f docker-compose.zerops.yaml run --rm -e FRAPPE_SITE_NAME_HEADER="$SITE_NAME" \
-e DB_HOST="$DB_HOST" -e DB_PORT="$DB_PORT" -e DB_PASSWORD="$DB_PASSWORD" \
-e ADMIN_PASSWORD="$ADMIN_PASSWORD" \
configurator bash -c '
echo "🏗️ Setting up Frappe configuration..."
# Navigate to bench directory
cd /home/frappe/frappe-bench
# Set up basic configuration
ls -1 apps > sites/apps.txt
bench set-config -g db_host $DB_HOST
bench set-config -gp db_port $DB_PORT
bench set-config -g redis_cache "redis://$REDIS_CACHE"
bench set-config -g redis_queue "redis://$REDIS_QUEUE"
bench set-config -g redis_socketio "redis://$REDIS_QUEUE"
bench set-config -gp socketio_port $SOCKETIO_PORT
echo "✅ Frappe configuration completed"
# Check if site already exists
if [ ! -d "sites/$FRAPPE_SITE_NAME_HEADER" ]; then
echo "🆕 Creating new site: $FRAPPE_SITE_NAME_HEADER"
bench new-site "$FRAPPE_SITE_NAME_HEADER" \
--mariadb-root-password "$DB_PASSWORD" \
--admin-password "$ADMIN_PASSWORD" \
--no-mariadb-socket
echo "✅ Site created successfully"
echo "📦 Installing ERPNext app..."
bench --site "$FRAPPE_SITE_NAME_HEADER" install-app erpnext
echo "✅ ERPNext installed successfully"
echo "🔧 Installing custom XML Importer app..."
if [ ! -d "apps/erpnext_xml_importer" ]; then
echo "📥 Downloading XML Importer app from GitHub..."
bench get-app https://github.com/UhrinDavid/erpnext_xml_importer.git
fi
bench --site "$FRAPPE_SITE_NAME_HEADER" install-app erpnext_xml_importer
echo "✅ XML Importer app installed successfully"
echo "🔄 Running site migration..."
bench --site "$FRAPPE_SITE_NAME_HEADER" migrate
echo "✅ Site migration completed"
echo "🎉 Site installation completed successfully!"
else
echo "♻️ Site $FRAPPE_SITE_NAME_HEADER already exists"
echo "🔄 Running migration to ensure site is up to date..."
bench --site "$FRAPPE_SITE_NAME_HEADER" migrate
echo "✅ Migration completed"
fi
'
echo "🎯 Frappe site installation script completed!"

View file

@ -1,75 +0,0 @@
#!/bin/bash
# Frappe/ERPNext Initial Setup Script for Zerops
# This script should be run after the services are deployed and running
set -e
echo "🚀 Starting Frappe/ERPNext setup on Zerops..."
# Check if required environment variables are set
if [ -z "$SITE_NAME" ]; then
echo "❌ Error: SITE_NAME environment variable is required"
echo " Set it to your domain name (e.g., mycompany.example.com)"
exit 1
fi
if [ -z "$DB_PASSWORD" ]; then
echo "❌ Error: DB_PASSWORD environment variable is required"
exit 1
fi
if [ -z "$ADMIN_PASSWORD" ]; then
echo "❌ Error: ADMIN_PASSWORD environment variable is required"
echo " This will be the password for the ERPNext admin user"
exit 1
fi
echo "📋 Configuration:"
echo " Site Name: $SITE_NAME"
echo " Database Host: ${DB_HOST:-db}"
echo " Admin Email: ${ADMIN_EMAIL:-administrator@$SITE_NAME}"
# Navigate to frappe bench directory
cd /home/frappe/frappe-bench
echo "🏗️ Creating new site: $SITE_NAME"
bench new-site "$SITE_NAME" \
--db-root-password "$DB_PASSWORD" \
--admin-password "$ADMIN_PASSWORD" \
--set-default
echo "📦 Installing ERPNext application..."
bench --site "$SITE_NAME" install-app erpnext
echo "👤 Setting up admin user..."
if [ -n "$ADMIN_EMAIL" ]; then
bench --site "$SITE_NAME" set-admin-password "$ADMIN_PASSWORD"
fi
echo "🔧 Final configuration..."
# Set site as default
bench use "$SITE_NAME"
# Clear cache
bench --site "$SITE_NAME" clear-cache
# Migrate database (in case there are any pending migrations)
bench --site "$SITE_NAME" migrate
echo "✅ Setup completed successfully!"
echo ""
echo "🌐 Your ERPNext site is ready at: $SITE_NAME"
echo "👤 Admin login: administrator"
echo "🔑 Admin password: [as set in ADMIN_PASSWORD]"
echo ""
echo "📚 Next steps:"
echo " 1. Configure your domain to point to this Zerops service"
echo " 2. Access your site and complete the setup wizard"
echo " 3. Configure SSL certificates if needed"
echo ""
echo "🔧 Useful commands:"
echo " - bench restart: Restart all processes"
echo " - bench --site $SITE_NAME console: Open Python console"
echo " - bench --site $SITE_NAME migrate: Run database migrations"
echo " - bench update: Update apps and migrate"

View file

@ -1,60 +1,78 @@
# Production Environment Configuration for Zerops Deployment
# Copy this file and customize the values for your deployment
# Zerops Environment Configuration Template
#
# This file shows the environment variables used in the Zerops deployment.
# DO NOT commit actual secrets to git! Set these in Zerops Dashboard.
# =============================================================================
# REQUIRED ZEROPS SECRETS (set these in Zerops Dashboard > Environment Variables)
# REQUIRED ZEROPS SECRETS (set in Zerops Dashboard > Environment Variables)
# =============================================================================
db_password=your-strong-database-password
admin_password=your-admin-password
site_name=your-domain.com
# Database credentials
dbPassword=your-secure-database-password-here
# Admin credentials
adminPassword=your-admin-password-here
# Site configuration
siteName=your-domain.com
# =============================================================================
# OPTIONAL CONFIGURATION (modify as needed)
# =============================================================================
ADMIN_EMAIL=admin@your-domain.com
ERPNEXT_VERSION=v15.84.0
# DOCKER COMPOSE CONFIGURATION (automatically configured)
# =============================================================================
# Database Configuration (managed by Zerops)
DB_HOST=db
DB_PORT=3306
# Redis Configuration (managed by Zerops)
REDIS_CACHE=redis-cache:6379
REDIS_QUEUE=redis-queue:6379
# Application Configuration
SOCKETIO_PORT=9000
FRAPPE_SITE_NAME_HEADER=your-domain.com
# Nginx/Proxy Configuration
UPSTREAM_REAL_IP_ADDRESS=127.0.0.1
UPSTREAM_REAL_IP_HEADER=X-Forwarded-For
UPSTREAM_REAL_IP_RECURSIVE=off
PROXY_READ_TIMEOUT=120s
CLIENT_MAX_BODY_SIZE=50m
# Frappe/ERPNext Image Configuration
# Image settings
CUSTOM_IMAGE=frappe/erpnext
CUSTOM_TAG=v15.84.0
ERPNEXT_VERSION=v15.84.0
PULL_POLICY=always
RESTART_POLICY=unless-stopped
# Backup Configuration (optional)
# BACKUP_S3_BUCKET=your-backup-bucket
# BACKUP_S3_ACCESS_KEY=your-access-key
# BACKUP_S3_SECRET_KEY=your-secret-key
# BACKUP_S3_REGION=your-region
# Service connections (managed by Zerops)
DB_HOST=db
DB_PORT=3306
REDIS_CACHE=redis-cache:6379
REDIS_QUEUE=redis-queue:6379
# Email Configuration (optional)
# MAIL_SERVER=smtp.your-email-provider.com
# MAIL_PORT=587
# MAIL_USERNAME=your-email@domain.com
# MAIL_PASSWORD=your-email-password
# MAIL_USE_TLS=true
# Frontend/Nginx settings
UPSTREAM_REAL_IP_ADDRESS=127.0.0.1
UPSTREAM_REAL_IP_HEADER=X-Forwarded-For
UPSTREAM_REAL_IP_RECURSIVE=off
# =============================================================================
# DEPLOYMENT ARCHITECTURE
# =============================================================================
# SSL/TLS Configuration (optional, if not using Zerops automatic SSL)
# LETSENCRYPT_EMAIL=your-email@domain.com
# SITES=your-domain.com
# Managed Services (created in Zerops Dashboard):
# - db: MariaDB 11 database service
# - redis-cache: Valkey 7.2 cache service (NON_HA mode)
# - redis-queue: Valkey 7.2 queue service (NON_HA mode)
# - app: Docker Compose application service
# Docker Compose Services:
# - configurator: One-time setup and configuration
# - backend: Main Frappe/ERPNext application (port 8000)
# - frontend: Nginx proxy (port 8080)
# - websocket: Socket.io service (port 9000)
# - queue-short: Background job worker (short tasks)
# - queue-long: Background job worker (long tasks)
# - scheduler: Cron job scheduler
# =============================================================================
# DEPLOYMENT INSTRUCTIONS
# =============================================================================
# 1. Create Zerops project and services:
# - MariaDB 11 service named "db"
# - Valkey 7.2 service named "redis-cache"
# - Valkey 7.2 service named "redis-queue"
#
# 2. Set environment secrets in Zerops Dashboard:
# - dbPassword: Strong database password
# - adminPassword: ERPNext admin password
# - siteName: Your domain name
#
# 3. Deploy using git integration or Zerops CLI
#
# 4. Access your ERPNext instance at the app service URL
# =============================================================================
# DEPLOYMENT INSTRUCTIONS

View file

@ -1,291 +1,132 @@
# Zerops YAML configuration for Frappe/ERPNext deployment
# This file defines the infrastructure and deployment settings for Zerops platform
# Documentation: https://docs.zerops.io/
# Zerops configuration for Frappe/ERPNext deployment
# Creates managed database services and uses Docker Compose for application containers
# Import this file in Zerops GUI to create the project with all required services
project:
name: frappe-erpnext
services:
zerops:
# MariaDB Database Service
- hostname: db
type: mariadb
version: "11"
mode: HA
priority: 10
# Zerops will automatically configure MariaDB with UTF8MB4
# Redis Cache Service
- hostname: redis-cache
type: redis
version: "6"
mode: HA
priority: 10
# Redis Queue Service
- hostname: redis-queue
type: redis
version: "6"
mode: HA
priority: 10
# Main Backend Application (Frappe/ERPNext)
- hostname: backend
type: python
version: "3.11"
buildFromGit: https://github.com/UhrinDavid/frappe_docker@zerops
enableSubdomainAccess: false
minContainers: 1
maxContainers: 3
autoscaling:
cpuThreshold: 70
memoryThreshold: 80
# Environment variables
- setup: db
base: mariadb@11
envSecrets:
DB_PASSWORD: ${db_password}
ADMIN_PASSWORD: ${admin_password}
ERPNEXT_VERSION: v15.84.0
DB_HOST: db
DB_PORT: 3306
REDIS_CACHE: redis-cache
REDIS_QUEUE: redis-queue
SOCKETIO_PORT: 9000
FRAPPE_SITE_NAME_HEADER: ${site_name}
CUSTOM_IMAGE: frappe/erpnext
CUSTOM_TAG: ${ERPNEXT_VERSION}
# Persistent volumes for site data
volumes:
- name: frappe-sites
mountPath: /home/frappe/frappe-bench/sites
size: 20GB
- name: frappe-logs
mountPath: /home/frappe/frappe-bench/logs
size: 5GB
# Build settings using production Dockerfile
build:
dockerfile: ./images/production/Containerfile
context: .
# Build-time commands to include site and apps in image
buildCommands:
- |
# Install any additional apps during build
# Example: bench get-app --branch main custom_app https://github.com/user/custom_app
echo "Building Frappe image with pre-installed apps..."
# Runtime initialization - deployment specific
runPrepareCommands:
- |
# Copy and execute the site initialization script
cp scripts/init-frappe-site.sh /home/frappe/init-frappe-site.sh
chmod +x /home/frappe/init-frappe-site.sh
chown frappe:frappe /home/frappe/init-frappe-site.sh
# Run the initialization script as frappe user
su - frappe -c "/home/frappe/init-frappe-site.sh"
# Health check
healthCheck:
httpGet:
port: 8000
path: /api/method/ping
initialDelaySeconds: 120
periodSeconds: 30
# Frontend/Nginx Proxy Service
- hostname: frontend
type: nginx
version: "1.24"
enableSubdomainAccess: true
minContainers: 1
maxContainers: 2
buildFromGit: https://github.com/UhrinDavid/frappe_docker@zerops
# Environment variables for nginx templating
envSecrets:
BACKEND: backend:8000
SOCKETIO: websocket:9000
FRAPPE_SITE_NAME_HEADER: ${site_name}
UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1
UPSTREAM_REAL_IP_HEADER: X-Forwarded-For
UPSTREAM_REAL_IP_RECURSIVE: "off"
PROXY_READ_TIMEOUT: 120s
CLIENT_MAX_BODY_SIZE: 50m
# Build nginx with custom configuration
build:
dockerfile: ./images/production/Containerfile
context: .
# Use the nginx configuration from the frappe image
runPrepareCommands:
- cp /opt/frappe/nginx-template.conf /etc/nginx/conf.d/default.conf.template
- envsubst '$BACKEND $SOCKETIO $FRAPPE_SITE_NAME_HEADER' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf
# Command to run nginx
start: nginx-entrypoint.sh
# Health check
healthCheck:
httpGet:
port: 8080
path: /api/method/ping
initialDelaySeconds: 30
periodSeconds: 10
# WebSocket Service
- hostname: websocket
type: nodejs
version: "18"
buildFromGit: https://github.com/UhrinDavid/frappe_docker@zerops
enableSubdomainAccess: false
minContainers: 1
maxContainers: 2
# Shared persistent volumes for site access
volumes:
- name: frappe-sites
mountPath: /home/frappe/frappe-bench/sites
size: 20GB
# Environment variables
envSecrets:
DB_HOST: db
DB_PORT: 3306
REDIS_CACHE: redis-cache
REDIS_QUEUE: redis-queue
SOCKETIO_PORT: 9000
build:
dockerfile: ./images/production/Containerfile
context: .
runPrepareCommands:
- |
# Copy and execute the service initialization script
cp scripts/init-service.sh /home/frappe/init-service.sh
chmod +x /home/frappe/init-service.sh
chown frappe:frappe /home/frappe/init-service.sh
# Run the initialization script as frappe user
su - frappe -c "/home/frappe/init-service.sh WebSocket"
# Command to run the WebSocket server
start: node /home/frappe/frappe-bench/apps/frappe/socketio.js
# Health check
healthCheck:
httpGet:
port: 9000
path: /socket.io/
initialDelaySeconds: 60
periodSeconds: 30
# Queue Worker - Short Tasks
- hostname: queue-short
type: python
version: "3.11"
buildFromGit: https://github.com/UhrinDavid/frappe_docker@zerops
enableSubdomainAccess: false
minContainers: 1
maxContainers: 3
# Shared persistent volumes for site access
volumes:
- name: frappe-sites
mountPath: /home/frappe/frappe-bench/sites
size: 20GB
# Environment variables
envSecrets:
DB_HOST: db
DB_PORT: 3306
REDIS_CACHE: redis-cache
REDIS_QUEUE: redis-queue
build:
dockerfile: ./images/production/Containerfile
context: .
runPrepareCommands:
- |
# Copy and execute the service initialization script
cp scripts/init-service.sh /home/frappe/init-service.sh
chmod +x /home/frappe/init-service.sh
chown frappe:frappe /home/frappe/init-service.sh
# Run the initialization script as frappe user
su - frappe -c "/home/frappe/init-service.sh 'Queue Worker (Short)'"
# Command to run the worker
start: bench worker --queue short,default
# Queue Worker - Long Tasks
- hostname: queue-long
type: python
version: "3.11"
buildFromGit: https://github.com/UhrinDavid/frappe_docker@zerops
enableSubdomainAccess: false
minContainers: 1
maxContainers: 2
# Shared persistent volumes for site access
volumes:
- name: frappe-sites
mountPath: /home/frappe/frappe-bench/sites
size: 20GB
# Environment variables
envSecrets:
DB_HOST: db
DB_PORT: 3306
REDIS_CACHE: redis-cache
REDIS_QUEUE: redis-queue
build:
dockerfile: ./images/production/Containerfile
context: .
runPrepareCommands:
- |
# Copy and execute the service initialization script
cp scripts/init-service.sh /home/frappe/init-service.sh
chmod +x /home/frappe/init-service.sh
chown frappe:frappe /home/frappe/init-service.sh
# Run the initialization script as frappe user
su - frappe -c "/home/frappe/init-service.sh 'Queue Worker (Long)'"
# Command to run the long queue worker
start: bench worker --queue long,default,short
# Scheduler Service
- hostname: scheduler
type: python
version: "3.11"
buildFromGit: https://github.com/UhrinDavid/frappe_docker@zerops
enableSubdomainAccess: false
minContainers: 1
maxContainers: 1
# Shared persistent volumes for site access
volumes:
- name: frappe-sites
mountPath: /home/frappe/frappe-bench/sites
size: 20GB
# Environment variables
envSecrets:
DB_HOST: db
DB_PORT: 3306
REDIS_CACHE: redis-cache
REDIS_QUEUE: redis-queue
build:
dockerfile: ./images/production/Containerfile
context: .
runPrepareCommands:
- |
# Copy and execute the service initialization script
cp scripts/init-service.sh /home/frappe/init-service.sh
chmod +x /home/frappe/init-service.sh
chown frappe:frappe /home/frappe/init-service.sh
# Run the initialization script as frappe user
su - frappe -c "/home/frappe/init-service.sh Scheduler"
# Command to run the scheduler
start: bench schedule
MARIADB_ROOT_PASSWORD: ${dbPassword}
# Required secrets that need to be set in Zerops dashboard:
# - db_password: Database password for MariaDB (e.g., "mySecureDbPassword123")
# - admin_password: ERPNext admin user password (e.g., "myAdminPassword123")
# - site_name: Your site domain name (e.g., "mycompany.example.com")
#
# This configuration includes:
# ✅ Clean Docker image with ERPNext apps (deployment-agnostic)
# ✅ Persistent volumes for site data, uploads, and configurations
# ✅ Deployment-specific initialization via runPrepareCommands
# ✅ Shared volumes across all services that need site access
#
# Benefits:
# - Docker image is reusable across different deployment platforms
# - Site initialization is handled at deployment time (not in image)
# - Container restarts preserve all site data and configurations
# - Apps are consistently deployed across all containers
# - No manual setup required after deployment
# - Fast container startup times
# - Proper separation between image and deployment concerns
# Redis Cache Service (using Valkey - Redis compatible)
- setup: redis-cache
base: valkey@7.2
mode: NON_HA # Use HA in production
# Redis Queue Service (using Valkey - Redis compatible)
- setup: redis-queue
base: valkey@7.2
mode: NON_HA # Use HA in production
# Frappe/ERPNext Application Services (Docker Compose)
- setup: app
build:
# Deploy the custom docker-compose file to the runtime
deployFiles: ./docker-compose.zerops.yaml
addToRunPrepare: ./docker-compose.zerops.yaml
# Copy initialization scripts
deployFiles:
- ./docker-compose.zerops.yaml
- ./scripts/
run:
# Environment variables for Docker Compose services
envVariables:
# Image configuration
CUSTOM_IMAGE: frappe/erpnext
CUSTOM_TAG: v15.84.0
ERPNEXT_VERSION: v15.84.0
PULL_POLICY: always
RESTART_POLICY: unless-stopped
# Database connection (using Zerops service)
DB_HOST: db
DB_PORT: 3306
DB_PASSWORD: ${dbPassword}
# Redis connections (using Zerops Valkey services)
REDIS_CACHE: redis-cache:6379
REDIS_QUEUE: redis-queue:6379
# Site configuration
FRAPPE_SITE_NAME_HEADER: ${siteName}
ADMIN_PASSWORD: ${adminPassword}
# Frontend/Nginx configuration
UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1
UPSTREAM_REAL_IP_HEADER: X-Forwarded-For
UPSTREAM_REAL_IP_RECURSIVE: "off"
PROXY_READ_TIMEOUT: 120
CLIENT_MAX_BODY_SIZE: 50m
# Site initialization and app installation
prepareCommands:
# Wait for database and Redis services
- |
echo "Waiting for database connection..."
while ! mariadb -hdb -P3306 -uroot -p${DB_PASSWORD} -e "SELECT 1;" 2>/dev/null; do
echo "Database not ready, waiting 5 seconds..."
sleep 5
done
echo "Database connection established"
- |
echo "Waiting for Redis cache (Valkey) connection..."
while ! redis-cli -h redis-cache -p 6379 ping 2>/dev/null; do
echo "Redis cache not ready, waiting 5 seconds..."
sleep 5
done
echo "Redis cache connection established"
- |
echo "Waiting for Redis queue (Valkey) connection..."
while ! redis-cli -h redis-queue -p 6379 ping 2>/dev/null; do
echo "Redis queue not ready, waiting 5 seconds..."
sleep 5
done
echo "Redis queue connection established"
# Pull Docker images
- docker compose -f docker-compose.zerops.yaml pull
# Install Frappe site and apps using dedicated script
- |
echo "🚀 Running site installation..."
chmod +x scripts/install-site.sh
./scripts/install-site.sh
echo "✅ Site installation completed"
# Start all application services
start: docker compose -f docker-compose.zerops.yaml up --force-recreate
# Expose the frontend port
ports:
- port: 8080
httpSupport: true
# Required secrets in Zerops dashboard:
# - dbPassword: Database password for MariaDB (e.g., "mySecureDbPassword123")
# - adminPassword: ERPNext admin user password (e.g., "myAdminPassword123")
# - siteName: Your site domain name (e.g., "mycompany.example.com")
#
# This configuration provides:
# ✅ Managed MariaDB service with automatic backups and scaling
# ✅ Dedicated Redis services for cache and queue (better performance)
# ✅ Docker Compose for Frappe containers only (simpler, faster)
# ✅ Automatic site initialization with custom apps
# ✅ Persistent storage managed by Zerops
# ✅ Service health monitoring and auto-restart
# ✅ Built-in service discovery (services connect by name)
#
# Architecture:
# - db: Managed MariaDB 11 service (default Zerops configuration)
# - redis-cache: Managed Valkey 7.2 service (default configuration, NON_HA mode)
# - redis-queue: Managed Valkey 7.2 service (default configuration, NON_HA mode)
# - app: Docker Compose with Frappe containers (backend, frontend, websocket, workers, scheduler)
#
# Benefits of managed services:
# - Automatic backups and point-in-time recovery
# - Built-in monitoring and alerting
# - Automatic security updates
# - High availability and failover
# - Performance optimization
# - No container overhead for databases