🧩 Guía de instalación de ERPNext en VPS (Ubuntu + Docker + Nginx)

📌 Requisitos previos

  • VPS con Ubuntu 24.04 (o similar)
  • Al menos 4 GB de RAM (recomendado 8 GB)
  • Docker y Docker Compose instalados
  • Nginx y Certbot instalados en el host
  • Un dominio o subdominio apuntando a la IP del VPS (en esta guía usamos erp.tudominio.com)

1. Preparar el entorno

bash

mkdir -p ~/erpnext && cd ~/erpnext

2. Archivo de variables de entorno (.env)

Crea el archivo sin espacios alrededor del =:

bash

cat > .env << 'EOF'
MYSQL_ROOT_PASSWORD=ContraseñaSegura123
ADMIN_PASSWORD=OtraContraseña456
SITE=erp.tudominio.com
MAIL_FROM=erp@tudominio.com
EOF

3. docker-compose.yml con configuración corregida de Redis

Copia este contenido exacto (ya incluye las variables correctas REDIS_CACHEREDIS_QUEUEREDIS_SOCKETIO):

yaml

services:
  db:
    image: mariadb:10.6
    container_name: erpnext-db
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - erp_network

  redis-cache:
    image: redis:7-alpine
    container_name: erpnext-redis-cache
    restart: unless-stopped
    command: redis-server --appendonly yes
    volumes:
      - redis-cache-data:/data
    networks:
      - erp_network

  redis-queue:
    image: redis:7-alpine
    container_name: erpnext-redis-queue
    restart: unless-stopped
    command: redis-server --appendonly yes
    volumes:
      - redis-queue-data:/data
    networks:
      - erp_network

  erpnext:
    image: frappe/erpnext:v15
    container_name: erpnext-web
    restart: unless-stopped
    depends_on:
      - db
      - redis-cache
      - redis-queue
    environment:
      - FRAPPE_DB_HOST=db
      - FRAPPE_DB_PORT=3306
      - FRAPPE_DB_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - REDIS_CACHE=redis://redis-cache:6379
      - REDIS_QUEUE=redis://redis-queue:6379
      - REDIS_SOCKETIO=redis://redis-cache:6379
      - FRAPPE_SITE_NAME_HEADER=${SITE}
      - FRAPPE_ADMIN_PASSWORD=${ADMIN_PASSWORD}
      - FRAPPE_SMTP_SERVER=172.17.0.1   # Gateway del host hacia Docker
      - FRAPPE_SMTP_PORT=25
      - FRAPPE_SMTP_LOGIN=
      - FRAPPE_SMTP_PASSWORD=
      - FRAPPE_MAIL_FROM=${MAIL_FROM}
    volumes:
      - sites-data:/home/frappe/frappe-bench/sites
    networks:
      - erp_network
    ports:
      - "127.0.0.1:8000:8000"

  # Workers y scheduler (necesarios)
  erpnext-queue-short:
    image: frappe/erpnext:v15
    container_name: erpnext-queue-short
    restart: unless-stopped
    command: bench worker --queue short
    depends_on:
      - erpnext
    environment:
      - FRAPPE_DB_HOST=db
      - FRAPPE_DB_PORT=3306
      - FRAPPE_DB_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - REDIS_CACHE=redis://redis-cache:6379
      - REDIS_QUEUE=redis://redis-queue:6379
      - REDIS_SOCKETIO=redis://redis-cache:6379
    volumes:
      - sites-data:/home/frappe/frappe-bench/sites
    networks:
      - erp_network

  erpnext-queue-long:
    image: frappe/erpnext:v15
    container_name: erpnext-queue-long
    restart: unless-stopped
    command: bench worker --queue long
    depends_on:
      - erpnext
    environment:
      - FRAPPE_DB_HOST=db
      - FRAPPE_DB_PORT=3306
      - FRAPPE_DB_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - REDIS_CACHE=redis://redis-cache:6379
      - REDIS_QUEUE=redis://redis-queue:6379
      - REDIS_SOCKETIO=redis://redis-cache:6379
    volumes:
      - sites-data:/home/frappe/frappe-bench/sites
    networks:
      - erp_network

  erpnext-queue-default:
    image: frappe/erpnext:v15
    container_name: erpnext-queue-default
    restart: unless-stopped
    command: bench worker --queue default
    depends_on:
      - erpnext
    environment:
      - FRAPPE_DB_HOST=db
      - FRAPPE_DB_PORT=3306
      - FRAPPE_DB_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - REDIS_CACHE=redis://redis-cache:6379
      - REDIS_QUEUE=redis://redis-queue:6379
      - REDIS_SOCKETIO=redis://redis-cache:6379
    volumes:
      - sites-data:/home/frappe/frappe-bench/sites
    networks:
      - erp_network

  erpnext-schedule:
    image: frappe/erpnext:v15
    container_name: erpnext-schedule
    restart: unless-stopped
    command: bench schedule
    depends_on:
      - erpnext
    environment:
      - FRAPPE_DB_HOST=db
      - FRAPPE_DB_PORT=3306
      - FRAPPE_DB_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - REDIS_CACHE=redis://redis-cache:6379
      - REDIS_QUEUE=redis://redis-queue:6379
      - REDIS_SOCKETIO=redis://redis-cache:6379
    volumes:
      - sites-data:/home/frappe/frappe-bench/sites
    networks:
      - erp_network

volumes:
  db-data:
  redis-cache-data:
  redis-queue-data:
  sites-data:

networks:
  erp_network:
    driver: bridge

4. Iniciar los servicios

bash

docker compose up -d

5. Crear el sitio de ERPNext

bash

source .env
docker exec -it erpnext-web bench new-site ${SITE} \
  --mariadb-root-password "${MYSQL_ROOT_PASSWORD}" \
  --admin-password "${ADMIN_PASSWORD}" \
  --db-host db \
  --install-app erpnext \
  --set-default

6. Configurar el correo saliente

bash

docker exec -it erpnext-web bench set-config --global smtp_server 172.17.0.1
docker exec -it erpnext-web bench set-config --global smtp_port 25
docker exec -it erpnext-web bench set-config --global smtp_mail_from ${MAIL_FROM}
docker exec -it erpnext-web bench clear-cache

7. Forzar la contraseña del administrador

bash

docker exec -it erpnext-web bench set-admin-password 'TuContraseñaFinal'

8. Construir los activos estáticos (CSS/JS)

bash

docker exec -it erpnext-web bench --site ${SITE} build --force

9. Servir los estáticos con Nginx (solución robusta)

Los estáticos se copian del contenedor al host resolviendo enlaces simbólicos con tar -h:

bash

# Copiar archivos reales del contenedor al host
sudo rm -rf /var/www/assets
sudo docker exec erpnext-web tar -ch -C /home/frappe/frappe-bench/sites assets | sudo tar -x -C /var/www

# Permisos para Nginx
sudo chown -R www-data:www-data /var/www/assets
sudo chmod -R a+rX /var/www/assets

10. Configurar Nginx como proxy inverso con SSL

Crea el archivo /etc/nginx/sites-available/erp:

nginx

server {
    listen 80;
    listen [::]:80;
    server_name erp.tudominio.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name erp.tudominio.com;

    ssl_certificate /etc/letsencrypt/live/erp.tudominio.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/erp.tudominio.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location /assets {
        alias /var/www/assets;
        expires max;
        add_header Cache-Control "public, immutable";
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        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;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Habilita el sitio y obtén el certificado SSL:

bash

sudo ln -s /etc/nginx/sites-available/erp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d erp.tudominio.com

11. Acceder

Abre https://erp.tudominio.com e inicia sesión con:

  • Usuario: Administrator
  • Contraseña: la que definiste en ADMIN_PASSWORD o con set-admin-password

🔁 Actualizar los estáticos después de cada bench build

Cada vez que ejecutes bench build, los bundles CSS/JS cambian de nombre y deben copiarse nuevamente al host para que Nginx los sirva. Usa este script de dos pasos:

bash

# Paso 1: Copiar desde el contenedor al host
sudo docker exec erpnext-web tar -ch -C /home/frappe/frappe-bench/sites assets | sudo tar -x -C /var/www

# Paso 2: Ajustar permisos
sudo chown -R www-data:www-data /var/www/assets
sudo chmod -R a+rX /var/www/assets
sudo systemctl reload nginx

✅ Resumen de puntos clave aprendidos

  • Usa siempre REDIS_CACHEREDIS_QUEUEREDIS_SOCKETIO como variables de entorno (no FRAPPE_REDIS_*).
  • El archivo .env no debe tener espacios alrededor del =.
  • El backend de ERPNext en producción no sirve estáticos; deben servirse con Nginx (u otro servidor).
  • Para copiar los estáticos al host resuelve los enlaces simbólicos con tar -h o docker cp seguido de un ajuste, pero el método tar -h es más limpio y directo.
  • Si al acceder ves la interfaz sin estilos, basta con ejecutar el paso de actualización de estáticos (punto 9 y el script del apartado anterior).
  • La integración con n8n (pendiente) se hará mediante la API de ERPNext, usando la URL http://erpnext-web:8000 desde el contenedor de n8n si están en la misma red Docker.

Comments

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *