// Índice de contenidos
Lanzar código directamente a producción sin pruebas es como saltar de un avión sin verificar el paracaídas. Puede funcionar, pero cuando falla, las consecuencias son desastrosas.
Un entorno de staging te permite probar cambios en condiciones reales antes de que lleguen a tus usuarios. Con HestiaCP, puedes configurar un flujo profesional de desarrollo → staging → producción sin herramientas externas complicadas.
He implementado este workflow en proyectos con más de 100,000 usuarios activos. Un error en producción puede costar miles de euros en pérdidas. Un entorno de staging bien configurado cuesta menos de €10/mes adicionales y evita esos desastres.
Qué es staging y por qué lo necesitas
Definición de entornos
| Entorno | Propósito | Acceso | Datos |
|---|---|---|---|
| Desarrollo | Escribir código nuevo | Solo desarrolladores | Datos de prueba |
| Staging | Probar antes de producción | Equipo + QA | Copia de producción |
| Producción | Usuarios reales | Público | Datos reales |
Por qué staging es crítico
| Sin Staging | Con Staging |
|---|---|
| Errores descubiertos por usuarios | Errores detectados antes del lanzamiento |
| Tiempo de caída impredecible | Despliegues controlados |
| Rollback caótico | Rollback planificado |
| Estrés en cada deploy | Confianza en cada release |
| Pérdida de clientes | Experiencia consistente |
ROI del staging
Coste de staging: ~€10/mes (VPS pequeño)
Coste de 1 hora de caída en e-commerce: €500-€50,000
Coste de perder 1 cliente por bug: €200-€2,000/año
ROI = Evitar 1 incidente grave al año = 50x-500x la inversión
Arquitectura de entornos
Opción 1: Mismo servidor (económico)
VPS HestiaCP (4GB RAM)
├── produccion.tudominio.com (usuario: prod)
├── staging.tudominio.com (usuario: staging)
└── dev.tudominio.com (usuario: dev)
Ventajas: Económico, fácil de gestionar Desventajas: Comparten recursos, staging puede afectar producción
Opción 2: Servidores separados (recomendado)
VPS Producción (8GB RAM)
└── tudominio.com
VPS Staging (2GB RAM)
└── staging.tudominio.com
Ventajas: Aislamiento total, staging puede ser más pequeño Desventajas: Coste adicional, sincronización más compleja
Opción 3: Híbrido (óptimo)
VPS Principal (8GB RAM)
├── tudominio.com (producción)
└── staging.tudominio.com (staging)
Local/Docker
└── dev.tudominio.local (desarrollo)
Comparativa de recursos
| Entorno | RAM | CPU | Disco | Coste aprox. |
|---|---|---|---|---|
| Producción | 4-16 GB | 2-4 vCPU | 50-200 GB | €15-€100/mes |
| Staging | 2-4 GB | 1-2 vCPU | 20-50 GB | €5-€20/mes |
| Desarrollo | 1-2 GB | 1 vCPU | 10-20 GB | Local/€5/mes |
Configurar staging en HestiaCP
Crear usuario de staging
# Crear usuario separado para staging
v-add-user staging password [email protected] default
# Crear dominio de staging
v-add-web-domain staging staging.tudominio.com
# Añadir SSL
v-add-letsencrypt-domain staging staging.tudominio.com
# Crear base de datos
v-add-database staging app_staging db_user password
Proteger staging con contraseña
Staging no debe ser público. Protégelo con autenticación básica:
# Crear archivo de contraseñas
htpasswd -c /home/staging/conf/web/.htpasswd equipo
# Editar configuración Nginx
nano /home/staging/conf/web/staging.tudominio.com/nginx.conf_custom
Añade:
location / {
auth_basic "Staging - Acceso Restringido";
auth_basic_user_file /home/staging/conf/web/.htpasswd;
# Permitir webhooks sin auth
location ~ ^/(webhook|deploy) {
auth_basic off;
# Validar por IP o token
allow 192.30.252.0/22; # GitHub
deny all;
}
}
# Aplicar cambios
v-rebuild-web-domain staging staging.tudominio.com
Restringir por IP (alternativa)
# Solo permitir IPs del equipo
allow 85.123.45.67; # Oficina
allow 192.168.1.0/24; # VPN
deny all;
Robots.txt para staging
Evita que Google indexe staging:
cat > /home/staging/web/staging.tudominio.com/public_html/robots.txt << 'EOF'
User-agent: *
Disallow: /
EOF
Cabeceras anti-indexación
# En nginx.conf_custom
add_header X-Robots-Tag "noindex, nofollow" always;
Sincronizar base de datos
Script de sincronización
Crea /home/staging/scripts/sync-db-from-prod.sh:
#!/bin/bash
# Configuración
PROD_DB="prod_app"
PROD_USER="prod_db_user"
PROD_PASS="prod_password"
PROD_HOST="localhost" # o IP del servidor de producción
STAGING_DB="staging_app_staging"
STAGING_USER="staging_db_user"
STAGING_PASS="staging_password"
BACKUP_DIR="/home/staging/backups"
DATE=$(date +%Y%m%d_%H%M%S)
# Crear directorio de backups
mkdir -p $BACKUP_DIR
echo "=== Sincronización DB: $(date) ==="
# 1. Backup de staging actual (por seguridad)
echo "Backup de staging actual..."
mysqldump -u $STAGING_USER -p$STAGING_PASS $STAGING_DB > $BACKUP_DIR/staging_pre_sync_$DATE.sql
# 2. Exportar producción
echo "Exportando producción..."
mysqldump -u $PROD_USER -p$PROD_PASS -h $PROD_HOST $PROD_DB > $BACKUP_DIR/prod_export_$DATE.sql
# 3. Importar a staging
echo "Importando a staging..."
mysql -u $STAGING_USER -p$STAGING_PASS $STAGING_DB < $BACKUP_DIR/prod_export_$DATE.sql
# 4. Sanitizar datos sensibles
echo "Sanitizando datos..."
mysql -u $STAGING_USER -p$STAGING_PASS $STAGING_DB << 'EOSQL'
-- Cambiar emails de usuarios
UPDATE users SET email = CONCAT('staging_', id, '@example.com') WHERE email NOT LIKE '%@tuempresa.com';
-- Limpiar tokens y sesiones
TRUNCATE TABLE sessions;
TRUNCATE TABLE password_resets;
-- Desactivar pasarelas de pago reales
UPDATE options SET value = 'sandbox' WHERE name = 'payment_mode';
-- Cambiar URLs
UPDATE options SET value = 'https://staging.tudominio.com' WHERE name = 'site_url';
EOSQL
# 5. Limpiar backups antiguos (más de 7 días)
find $BACKUP_DIR -name "*.sql" -mtime +7 -delete
echo "=== Sincronización completada: $(date) ==="
chmod +x /home/staging/scripts/sync-db-from-prod.sh
Sincronización para WordPress
#!/bin/bash
# sync-wp-db.sh
PROD_DB="prod_wordpress"
STAGING_DB="staging_wordpress"
# Exportar producción
mysqldump -u root -p$PROD_DB > /tmp/wp_prod.sql
# Importar a staging
mysql -u root -p$STAGING_DB < /tmp/wp_prod.sql
# Buscar y reemplazar URLs
cd /home/staging/web/staging.tudominio.com/public_html
wp search-replace 'https://tudominio.com' 'https://staging.tudominio.com' --all-tables
# Desactivar plugins problemáticos en staging
wp plugin deactivate woocommerce-gateway-stripe # Evitar pagos reales
wp plugin activate woocommerce-gateway-test
# Limpiar caché
wp cache flush
Sincronización para Laravel
#!/bin/bash
# sync-laravel-db.sh
# Exportar producción
mysqldump -u root prod_laravel > /tmp/laravel_prod.sql
# Importar a staging
mysql -u root staging_laravel < /tmp/laravel_prod.sql
# Ejecutar migraciones pendientes
cd /home/staging/web/staging.tudominio.com/public_html
php artisan migrate --force
# Limpiar sesiones y caché
php artisan session:clear
php artisan cache:clear
php artisan config:clear
# Regenerar key (diferente a producción)
php artisan key:generate
Staging profesional con VPS
El VPS HestiaCP es perfecto para staging: crea entornos separados, sincroniza con producción y despliega con confianza.
Sincronizar archivos
Rsync para sincronización
#!/bin/bash
# sync-files-from-prod.sh
PROD_PATH="/home/prod/web/tudominio.com/public_html"
STAGING_PATH="/home/staging/web/staging.tudominio.com/public_html"
# Sincronizar uploads/media (solo archivos, no código)
rsync -avz --delete \
--exclude='*.php' \
--exclude='.git' \
--exclude='node_modules' \
--exclude='vendor' \
--exclude='.env' \
$PROD_PATH/wp-content/uploads/ \
$STAGING_PATH/wp-content/uploads/
# Ajustar permisos
chown -R staging:staging $STAGING_PATH/wp-content/uploads/
Solo código (sin uploads)
Para sincronizar solo código desde Git:
#!/bin/bash
# deploy-code-to-staging.sh
cd /home/staging/web/staging.tudominio.com/public_html
# Obtener última versión
git fetch origin
git reset --hard origin/staging
# Instalar dependencias
composer install --no-dev
npm ci && npm run build
# Migraciones
php artisan migrate --force
# Limpiar caché
php artisan cache:clear
php artisan config:cache
php artisan route:cache
Sincronización bidireccional (peligroso)
Solo para casos específicos donde staging genera contenido:
# ⚠️ Usar con precaución
rsync -avz --update \
staging:/home/staging/web/staging.tudominio.com/public_html/uploads/ \
/home/prod/web/tudominio.com/public_html/uploads/
Variables de entorno
Estructura de .env
Producción (/home/prod/.env):
APP_ENV=production
APP_DEBUG=false
APP_URL=https://tudominio.com
DB_HOST=localhost
DB_DATABASE=prod_app
DB_USERNAME=prod_user
DB_PASSWORD=super_secreto_123
MAIL_MAILER=smtp
MAIL_HOST=mail.tudominio.com
STRIPE_KEY=sk_live_xxxxx
STRIPE_SECRET=pk_live_xxxxx
Staging (/home/staging/.env):
APP_ENV=staging
APP_DEBUG=true
APP_URL=https://staging.tudominio.com
DB_HOST=localhost
DB_DATABASE=staging_app
DB_USERNAME=staging_user
DB_PASSWORD=staging_pass_456
MAIL_MAILER=log # No enviar emails reales
# O usar Mailtrap
MAIL_HOST=smtp.mailtrap.io
MAIL_USERNAME=tu_mailtrap_user
STRIPE_KEY=sk_test_xxxxx
STRIPE_SECRET=pk_test_xxxxx
Verificar entorno en código
<?php
// Laravel
if (app()->environment('staging')) {
// Código específico de staging
}
// WordPress
if (defined('WP_ENV') && WP_ENV === 'staging') {
// Código específico de staging
}
// PHP genérico
if (getenv('APP_ENV') === 'staging') {
// Código específico de staging
}
Prevenir acciones peligrosas en staging
<?php
// Evitar envío de emails reales en staging
if (getenv('APP_ENV') === 'staging') {
// Redirigir todos los emails a log
config(['mail.default' => 'log']);
// O a una dirección de prueba
config(['mail.to.address' => '[email protected]']);
}
// Evitar webhooks a servicios externos
if (getenv('APP_ENV') !== 'production') {
// No enviar a Stripe, PayPal, etc.
return;
}
Workflow de despliegue
Flujo recomendado
1. Desarrollador → git push feature/nueva-funcion
↓
2. CI/CD → Tests automáticos
↓
3. Merge → staging branch
↓
4. Deploy automático → staging.tudominio.com
↓
5. QA → Pruebas manuales en staging
↓
6. Aprobación → Merge a main/production
↓
7. Deploy → tudominio.com (producción)
Branches y entornos
| Branch | Entorno | Deploy |
|---|---|---|
feature/* | Local/Dev | Manual |
develop | Desarrollo | Automático |
staging | Staging | Automático |
main | Producción | Manual/Aprobación |
Reglas de despliegue
- Nunca deploy directo a producción sin pasar por staging
- No deployar viernes tarde ni fines de semana
- Siempre tener backup antes de deploy a producción
- Monitorizar durante 1 hora después del deploy
Automatizar despliegues
Script de deploy a staging
/home/staging/scripts/deploy.sh:
#!/bin/bash
set -e # Salir si hay error
PROJECT_DIR="/home/staging/web/staging.tudominio.com/public_html"
LOG_FILE="/var/log/deploy-staging.log"
SLACK_WEBHOOK="https://hooks.slack.com/services/xxx"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE
}
notify_slack() {
curl -s -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"$1\"}" \
$SLACK_WEBHOOK
}
log "=== INICIO DEPLOY STAGING ==="
notify_slack "🚀 Iniciando deploy a staging..."
cd $PROJECT_DIR
# Modo mantenimiento
log "Activando modo mantenimiento..."
php artisan down --message="Actualizando..." --retry=60
# Backup rápido
log "Creando backup..."
tar -czf /tmp/staging-backup-$(date +%Y%m%d%H%M%S).tar.gz . 2>/dev/null || true
# Pull código
log "Obteniendo código..."
git fetch origin
git reset --hard origin/staging
# Dependencias
log "Instalando dependencias PHP..."
composer install --no-dev --optimize-autoloader
log "Instalando dependencias Node..."
npm ci
npm run build
# Migraciones
log "Ejecutando migraciones..."
php artisan migrate --force
# Caché
log "Limpiando caché..."
php artisan cache:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache
# Permisos
log "Ajustando permisos..."
chown -R staging:staging $PROJECT_DIR
find $PROJECT_DIR -type d -exec chmod 755 {} \;
find $PROJECT_DIR -type f -exec chmod 644 {} \;
# Salir de mantenimiento
log "Desactivando modo mantenimiento..."
php artisan up
log "=== DEPLOY STAGING COMPLETADO ==="
notify_slack "✅ Deploy a staging completado exitosamente"
Webhook para GitHub
/home/staging/web/staging.tudominio.com/public_html/webhook.php:
<?php
// Verificar firma de GitHub
$secret = getenv('GITHUB_WEBHOOK_SECRET');
$signature = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? '';
$payload = file_get_contents('php://input');
$expectedSignature = 'sha256=' . hash_hmac('sha256', $payload, $secret);
if (!hash_equals($expectedSignature, $signature)) {
http_response_code(403);
die('Firma inválida');
}
// Verificar que es push a staging
$data = json_decode($payload, true);
$ref = $data['ref'] ?? '';
if ($ref !== 'refs/heads/staging') {
die('No es branch staging');
}
// Ejecutar deploy
$output = shell_exec('/home/staging/scripts/deploy.sh 2>&1');
// Log
file_put_contents('/var/log/webhook.log', date('Y-m-d H:i:s') . "\n" . $output . "\n\n", FILE_APPEND);
echo "Deploy ejecutado";
Deploy a producción (con confirmación)
#!/bin/bash
# deploy-production.sh
echo "⚠️ DEPLOY A PRODUCCIÓN"
echo "======================="
echo ""
read -p "¿Has probado los cambios en staging? (yes/no): " TESTED
if [ "$TESTED" != "yes" ]; then
echo "❌ Debes probar primero en staging"
exit 1
fi
read -p "¿Hay backup reciente de producción? (yes/no): " BACKUP
if [ "$BACKUP" != "yes" ]; then
echo "Creando backup..."
/home/prod/scripts/backup-full.sh
fi
read -p "⚠️ CONFIRMAR DEPLOY A PRODUCCIÓN (escribir 'DEPLOY'): " CONFIRM
if [ "$CONFIRM" != "DEPLOY" ]; then
echo "❌ Deploy cancelado"
exit 1
fi
echo "🚀 Iniciando deploy a producción..."
# ... resto del script similar a staging
Rollback y recuperación
Rollback rápido con Git
#!/bin/bash
# rollback.sh
cd /home/prod/web/tudominio.com/public_html
# Ver últimos commits
echo "Últimos 5 commits:"
git log --oneline -5
read -p "Hash del commit al que volver: " COMMIT_HASH
# Crear backup del estado actual
git stash
# Volver al commit
git checkout $COMMIT_HASH
# Reinstalar dependencias de esa versión
composer install --no-dev
# Limpiar caché
php artisan cache:clear
php artisan config:cache
echo "Rollback completado a $COMMIT_HASH"
Rollback de base de datos
#!/bin/bash
# rollback-db.sh
BACKUP_DIR="/home/prod/backups"
echo "Backups disponibles:"
ls -la $BACKUP_DIR/*.sql.gz | tail -10
read -p "Archivo de backup a restaurar: " BACKUP_FILE
# Verificar archivo
if [ ! -f "$BACKUP_FILE" ]; then
echo "❌ Archivo no encontrado"
exit 1
fi
# Backup del estado actual antes de rollback
echo "Creando backup del estado actual..."
mysqldump -u root prod_app | gzip > $BACKUP_DIR/pre_rollback_$(date +%Y%m%d%H%M%S).sql.gz
# Restaurar
echo "Restaurando backup..."
gunzip < $BACKUP_FILE | mysql -u root prod_app
echo "✅ Base de datos restaurada"
Blue-Green Deployment (avanzado)
# Estructura
/home/prod/web/tudominio.com/
├── current -> releases/v1.5.2 # Symlink a versión actual
├── releases/
│ ├── v1.5.0/
│ ├── v1.5.1/
│ └── v1.5.2/
└── shared/
├── .env
├── storage/
└── uploads/
# Deploy nueva versión
NEW_VERSION="v1.5.3"
mkdir releases/$NEW_VERSION
git clone --branch $NEW_VERSION repo releases/$NEW_VERSION
ln -s ../../shared/.env releases/$NEW_VERSION/.env
ln -s ../../shared/storage releases/$NEW_VERSION/storage
# Cambiar a nueva versión (instantáneo)
ln -sfn releases/$NEW_VERSION current
# Rollback (instantáneo)
ln -sfn releases/v1.5.2 current
Testing en staging
Tests automáticos antes de deploy
# .github/workflows/test.yml
name: Test antes de deploy
on:
push:
branches: [staging, main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
- name: Install dependencies
run: composer install
- name: Run tests
run: php artisan test
- name: Run static analysis
run: ./vendor/bin/phpstan analyse
Checklist de testing manual
## Testing en Staging
### Funcionalidad básica
- [ ] Login/Logout funciona
- [ ] Registro de usuarios
- [ ] Recuperar contraseña
- [ ] Páginas principales cargan
### E-commerce (si aplica)
- [ ] Añadir al carrito
- [ ] Proceso de checkout
- [ ] Pasarela de pago en modo TEST
- [ ] Emails de confirmación (en Mailtrap)
### Rendimiento
- [ ] TTFB < 500ms
- [ ] Páginas cargan < 3s
- [ ] Sin errores en consola
### SEO
- [ ] Meta tags correctos
- [ ] robots.txt bloquea indexación
- [ ] URLs correctas (staging.xxx)
### Seguridad
- [ ] HTTPS funciona
- [ ] Headers de seguridad presentes
- [ ] Formularios tienen CSRF
Tests de carga
# Usando Apache Bench
ab -n 1000 -c 50 https://staging.tudominio.com/
# Usando wrk
wrk -t12 -c400 -d30s https://staging.tudominio.com/
Checklist pre-producción
Antes del deploy
## Pre-Deploy Checklist
### Código
- [ ] Todos los tests pasan
- [ ] Code review aprobado
- [ ] Staging probado y aprobado
- [ ] Sin console.log / dd() / var_dump
- [ ] .env de producción actualizado (si hay cambios)
### Base de datos
- [ ] Migraciones probadas en staging
- [ ] Backup de producción creado
- [ ] Plan de rollback de migraciones
### Infraestructura
- [ ] Suficiente espacio en disco
- [ ] Recursos del servidor normales
- [ ] Certificado SSL válido
- [ ] DNS configurado correctamente
### Comunicación
- [ ] Equipo informado del deploy
- [ ] Soporte preparado para incidencias
- [ ] Rollback plan documentado
Durante el deploy
## Durante Deploy
- [ ] Modo mantenimiento activado
- [ ] Monitorización activa
- [ ] Terminal con logs en vivo
- [ ] Backup verificado
Después del deploy
## Post-Deploy
### Inmediato (0-15 min)
- [ ] Sitio carga correctamente
- [ ] Login funciona
- [ ] Funcionalidades críticas OK
- [ ] Sin errores en logs
- [ ] Monitorización sin alertas
### Corto plazo (1 hora)
- [ ] Usuarios reportan OK
- [ ] Métricas de rendimiento normales
- [ ] Emails se envían correctamente
- [ ] Pagos procesan correctamente
### Documentación
- [ ] Changelog actualizado
- [ ] Incidencias documentadas
- [ ] Lecciones aprendidas
Preguntas frecuentes
¿Staging debe ser idéntico a producción?
Idealmente sí, pero puede tener menos recursos. Lo crítico es tener la misma versión de PHP, base de datos y configuración del servidor web.
¿Cada cuánto sincronizar staging con producción?
Depende del proyecto. Para e-commerce activo, casi diario. Para sitios estáticos, puede ser semanal o antes de grandes cambios.
¿Puedo usar datos reales en staging?
Sí, pero SIEMPRE sanitizados. Nunca expongas datos privados de clientes (PII) o tarjetas de crédito reales en entornos de prueba.
¿Qué hago si staging falla pero producción funciona?
Revisa las diferencias de entorno. A menudo es una configuración de PHP, un permiso de archivo o una variable de entorno incorrecta.
¿Necesito staging para proyectos pequeños?
Incluso para proyectos pequeños, tener un entorno donde romper cosas sin miedo es invaluable. HestiaCP hace que sea casi gratis tenerlo.
Conclusión
Un workflow profesional de staging → producción con HestiaCP incluye:
- Entornos separados con configuración idéntica
- Sincronización controlada de código y datos
- Variables de entorno para diferenciar entornos
- Automatización de despliegues con webhooks
- Rollback preparado para recuperación rápida
- Testing estructurado antes de cada release
- Checklist para no olvidar nada crítico
Este flujo te da confianza en cada despliegue y minimiza el riesgo de errores en producción.
Para un entorno de staging y producción profesional, el VPS HestiaCP de Avantys incluye la configuración necesaria, backups automáticos y soporte técnico especializado para resolver cualquier incidencia.
Guías relacionadas
VPS HestiaCP Administrado
Tu servidor HestiaCP con soporte técnico profesional. Nos encargamos de la administración para que tú te dediques a tus clientes.
VPS HestiaCP Administrado
La potencia de un VPS con el panel HestiaCP, montado y gestionado por nosotros.