Why Self-Host Symfony on a VPS?
Symfony requires precise PHP configuration: extensions (intl, opcache, pdo), PHP version, memory settings, and permissions on the var/cache and var/log folders. Shared hosting often imposes frozen versions and disables essential extensions, while a PaaS hides the PHP-FPM layer. On a VPS, you choose PHP 8.2/8.3, enable and tune OPcache and preloading for major performance gains, and configure PHP-FPM (number of processes, pm.max_children) according to your load. You also control MySQL or PostgreSQL and the Redis cache for sessions and Doctrine. For a production Symfony application, this level of control directly determines response time.
Concrete Benefits of a Self-Hosted Symfony
- Choice of the PHP version and all required extensions without restriction.
- OPcache and Symfony preloading enabled for significantly reduced response times.
- Fine-tuning of PHP-FPM (
pm.max_children, workers) according to available RAM. - MySQL/PostgreSQL database and Redis cache under your control, with scheduled backups.
- Execution of console commands and Messenger (workers) continuously via systemd or Supervisor.
- Atomic deployments with symbolic links, with no service interruption.
Hardware and Software Prerequisites
A Symfony application with a database runs comfortably on 2 vCPU and 2 GB of RAM; go up to 4 GB if you add Redis, Elasticsearch, or Messenger workers. Install PHP 8.2+ with FPM and the intl, opcache, pdo_mysql, mbstring, xml extensions, along with Composer 2. Nginx serves as the front end, with MySQL 8 or PostgreSQL as the database. You can containerize everything with Docker (php:8.3-fpm image) or install natively. A domain pointed to the VPS is required for TLS. Plan for the APP_ENV=prod environment and asset generation via AssetMapper or Webpack Encore.
Deploy Symfony Step by Step
Prepare the Server and the Code
Over SSH, install PHP-FPM 8.3, the extensions, and Composer (or Docker). Clone the repository and create .env.local with APP_ENV=prod, APP_SECRET, and DATABASE_URL. Run composer install --no-dev --optimize-autoloader.
Compile the Cache and Assets
Run php bin/console cache:clear --env=prod, then cache:warmup. Compile the assets with php bin/console asset-map:compile (AssetMapper) or npm run build (Encore). Check the permissions on var/.
Migrate the Database
Run php bin/console doctrine:migrations:migrate --no-interaction to apply the schema. Configure a Redis connection for sessions and the Doctrine cache if your application needs it.
Configure Nginx and PHP-FPM
Point the Nginx root to public/, route all requests to index.php via try_files, and pass the .php files to the PHP-FPM socket with fastcgi_pass. Set pm.max_children according to RAM and enable OPcache in php.ini.
Secure with an SSL Certificate
Obtain a Let's Encrypt certificate via Certbot for yourdomain.com, force HTTPS, and enable automatic renewal. Block direct access to the config/, src/, and var/ folders at the Nginx level.
Supervise the Messenger Workers
If you use the Messenger component, declare a systemd or Supervisor service that runs php bin/console messenger:consume async continuously, with automatic restart. Schedule a cron job for messenger:stop-workers after deployment in order to reload the new code.
Enable Symfony preloading by pointing opcache.preload to var/cache/prod/App_KernelProdContainer.preload.php in your php.ini. The service container and the framework classes are then loaded once into memory at PHP-FPM startup, which eliminates a significant part of the autoloading cost on each request and noticeably reduces latency in production.
Deploy from the ServOrbit Marketplace
The Symfony Stack template is available directly from the ServOrbit Marketplace. Order a VPS, select the template, and the provisioning script installs PHP 8.3-FPM, Nginx, PostgreSQL 16, Redis, Supervisor and Composer in a few minutes, without a manual SSH session for the base setup. The environment follows the official Symfony production recommendations: OPcache preloading enabled, PHP-FPM with tunable pm.max_children, Supervisor configured for Messenger workers.
Once the VPS is provisioned, clone your repository into /var/www/your-app, create your .env.local with APP_ENV=prod, APP_SECRET and DATABASE_URL, then run composer install --no-dev --optimize-autoloader. Apply your migrations with php bin/console doctrine:migrations:migrate --no-interaction and compile the cache with cache:clear --env=prod && cache:warmup.