¿Por qué Symfony en VPS en lugar de hosting compartido?
El hosting compartido te obliga a usar la versión de PHP del servidor, deshabilita extensiones consideradas 'peligrosas' e impide ejecutar procesos persistentes en segundo plano. Symfony 7 requiere PHP 8.2 como mínimo — un requisito que muchos hostings compartidos aún no cumplen. En un VPS, eliges PHP 8.3 desde el primer día, configuras OPcache con precisión e instalas ext-redis, ext-intl o ext-amqp según tus necesidades.
Los workers asíncronos (Symfony Messenger) requieren un proceso persistente gestionado por Supervisor o systemd. En hosting compartido, esto es simplemente imposible. En un VPS, arranca con un comando y se reinicia automáticamente tras un fallo.
Lo que un VPS desbloquea para Symfony
- Acceso root completo — instala cualquier extensión PHP, configura OPcache línea por línea y modifica php.ini sin abrir un ticket de soporte
- PHP 8.3 nativo — aprovecha las typed class constants, el atributo
#[Override]y el JIT mejorado sin esperar la migración del servidor compartido - Supervisor y workers persistentes — ejecuta
messenger:consumede forma continua con reinicio automático y rotación de logs para procesar tus colas - Redis local — instala Redis en el mismo VPS para sesiones, caché de Doctrine y transportes de Messenger, eliminando la latencia de red hacia un servicio externo
- Multi-versión PHP con ondrej/php — mantén PHP 8.1 para una app legacy y PHP 8.3 para la nueva en el mismo host con pools FPM separados
- Xdebug y profiling en staging — activa Blackfire o Xdebug en el entorno de review sin afectar la producción, e instala la herramienta en producción solo cuando sea necesario
- Despliegues atómicos con Deployer — cero tiempo de inactividad gracias a symlinks, rollback en un comando integrado en tu pipeline CI/CD
Requisitos concretos — recursos y software
Sistema operativo: Ubuntu 24.04 LTS o Debian 12.
PHP: 8.3 recomendado (mínimo 8.2 para Symfony 7). Extensiones requeridas: php8.3-cli, php8.3-fpm, php8.3-mbstring, php8.3-xml, php8.3-curl, php8.3-zip, php8.3-intl, php8.3-opcache.
Composer: versión 2.x obligatoria.
Base de datos: MySQL 8.0+ o PostgreSQL 16+.
RAM mínima:
- 1 GB: desarrollo o app ligera sin colas
- 2 GB: producción con Symfony Messenger y un transporte Redis
- 4 GB: workers múltiples, Redis, sesiones de alta frecuencia
Puertos a abrir: 80, 443, 22. Cierra todo lo demás.
Instalar PHP 8.3 y Symfony CLI en Ubuntu VPS
Actualizar el sistema e instalar dependencias previas
apt update && apt upgrade -y && apt install -y curl git unzip software-properties-commonAgregar el repositorio ondrej/php
add-apt-repository ppa:ondrej/php && apt updateInstalar PHP 8.3-FPM y las extensiones requeridas por Symfony
apt install -y php8.3-fpm php8.3-cli php8.3-mbstring php8.3-xml php8.3-curl php8.3-zip php8.3-intl php8.3-opcache php8.3-redis php8.3-mysqlVerificar la versión instalada y el estado del servicio FPM
php --version && systemctl status php8.3-fpmInstalar Composer 2.x de forma global
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && composer --versionInstalar la Symfony CLI
curl -1sLf 'https://dl.cloudsmith.io/public/symfony/stable/setup.deb.sh' | bash && apt install -y symfony-cliVerificar que se cumplen todos los requisitos de Symfony
symfony check:requirementsInstalar nginx y Redis
apt install -y nginx redis-server && systemctl enable --now nginx redis-server
Desplegar una app Symfony en producción con Deployer PHP
En tu proyecto Symfony local, agrega Deployer como dependencia de desarrollo
composer require deployer/deployer --devInicializar la configuración de Deployer (elegir la receta `symfony`)
./vendor/bin/dep initEditar `deploy.php` en la raíz del proyecto
require 'recipe/symfony.php';
set('repository', '[email protected]:tu-org/tu-app.git');
host('tu-vps.example.com')
->set('remote_user', 'deploy')
->set('deploy_path', '/var/www/symfony-app');En el VPS, crear el usuario de despliegue
useradd -m -s /bin/bash deploy && usermod -aG www-data deployEjecutar el primer despliegue desde tu máquina local
./vendor/bin/dep deploy productionVerificar que el symlink `current/` apunta a la última release
ls -la /var/www/symfony-app/Si algo falla, hacer rollback inmediato
./vendor/bin/dep rollback production
Configuración de nginx para Symfony
Crea /etc/nginx/sites-available/symfony-app con este bloque server mínimo:
server {
listen 80;
server_name tu-dominio.com;
root /var/www/symfony-app/current/public;
index index.php;
location / {
try_files $uri /index.php$is_args$args;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
}
location ~ /\.ht {
deny all;
}
}Activa el sitio y prueba la configuración antes de recargar nginx:ln -s /etc/nginx/sites-available/symfony-app /etc/nginx/sites-enabled/ && nginx -t && systemctl reload nginx
Para HTTPS usa Certbot: certbot --nginx -d tu-dominio.com.
Variables de entorno y seguridad
Symfony lee .env, luego .env.local, luego .env.prod.local. En producción, todos los valores sensibles deben estar en .env.local — nunca commitados en git.
Variables esenciales en producción:
APP_ENV=prod
APP_DEBUG=false
APP_SECRET=<64-chars-hex-aleatorio>
DATABASE_URL="mysql://dbuser:contraseñ[email protected]:3306/symfony_prod"
MAILER_DSN=smtp://user:[email protected]:587
MESSENGER_TRANSPORT_DSN=redis://127.0.0.1:6379/messagesGenera APP_SECRET aleatoriamente: openssl rand -hex 32. Nunca reutilices el valor por defecto del proyecto.
APP_DEBUG=false es crítico: en modo debug, Symfony expone trazas de pila completas y consultas SQL en el navegador — una filtración grave en producción.
Colas asíncronas con Symfony Messenger y Supervisor
Symfony Messenger difiere las tareas pesadas (envío de emails, generación de PDFs, webhooks) a una cola procesada de forma asíncrona. En un VPS, Supervisor garantiza que el worker funcione continuamente y se reinicie tras un fallo.
Instala Supervisor:apt install -y supervisor
Crea /etc/supervisor/conf.d/symfony-messenger.conf:
[program:symfony-messenger]
command=/var/www/symfony-app/current/bin/console messenger:consume async --time-limit=3600
user=deploy
autostart=true
autorestart=true
startretries=3
redirect_stderr=true
stdout_logfile=/var/log/supervisor/symfony-messenger.logActiva e inicia el worker:supervisorctl reread && supervisorctl update && supervisorctl start symfony-messenger
Monitorea el estado: supervisorctl status symfony-messenger
Hardening en 3 comandos. Bloquea el acceso a .git/ en nginx (agrega location ~ /\.git { deny all; } a tu bloque server). Instala fail2ban para bloquear escaneos SSH: apt install -y fail2ban. Restringe los permisos del directorio de despliegue: chmod 750 /var/www/symfony-app && chown -R deploy:www-data /var/www/symfony-app. Configura OPcache con opcache.validate_timestamps=0 en producción para máximo rendimiento.
Resolución de problemas — los 4 errores más frecuentes
1. The environment variable "APP_SECRET" is not set.
APP_SECRET falta en .env.local en el servidor. Verifica que el archivo existe en {{deploy_path}}/shared/ y contiene esta variable.
2. Error 500 silencioso — logs vacíos
Con APP_DEBUG=false, Symfony escribe en var/log/prod.log. Lee este archivo en tiempo real: tail -f /var/www/symfony-app/current/var/log/prod.log. Un error de permisos en var/cache/ o var/log/ es la causa más frecuente — corrígelo con chown -R deploy:www-data var/.
3. Connection refused en la base de datos
DATABASE_URL apunta a 127.0.0.1 pero MySQL escucha en localhost (socket Unix) — o al revés. Prueba directamente: mysql -u dbuser -p -h 127.0.0.1 symfony_db.
4. No hay comandos definidos en el namespace "cache"
Symfony no puede escribir en var/cache/. Solución: chmod -R 775 var/cache var/log && chown -R deploy:www-data var/.
Desplegar desde el Marketplace de ServOrbit
Si prefieres evitar horas de configuración manual, la plantilla symfony-stack del Marketplace de ServOrbit instala y configura automáticamente toda la infraestructura descrita en esta guía: PHP 8.3-FPM, Composer, nginx optimizado para Symfony, Redis, Supervisor y los permisos correctos. El VPS está operativo en menos de 5 minutos, con acceso SSH root inmediato para personalizar tu despliegue.
Mantienes el control total: el VPS es tuyo, no hay lock-in, y puedes usar Deployer PHP exactamente como se describe arriba desde la primera conexión SSH.