The Ultimate Guide to Stress-Free Symfony App Management with Supervisord

Jakub Skowron (skowron.dev)
7 min readOct 4, 2023

--

Photo by Bobur Mavlonov on Unsplash

Imagine a developer named Jack who is building an application in Symfony. With each passing day, he adds new features, and his application becomes more complex. At one point, he decided to use Symfony Messenger to better manage asynchronous tasks. However, with the growing number of consumers and queues, Jack noticed that managing them becomes increasingly challenging. He had to manually start, monitor, and restart each consumer, which was time-consuming and inefficient.

Enter Supervisord — a process management tool for UNIX systems. Supervisord can help Jack automate the management of consumers, migrations, cache clearing, and other background tasks. In this article, we will show how to implement Supervisord in a Symfony project to optimize and automate process management.

Installing Supervisord

> Installation in a Docker Image
To install Supervisord in a Docker image, you can follow these steps:

In the Dockerfile, add:

RUN apt-get update && apt-get install -y supervisor

Create a directory for Supervisord configuration files:

RUN mkdir -p /var/log/supervisor

Copy the main Supervisord configuration file into the image:

COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

> Installation on a System with Internet Access
If the system where you want to install Supervisord has internet access, you can install it using pip:

pip install supervisor

If your system doesn’t have pip, you need to download the Supervisord distribution and install it manually. Current and previous versions of Supervisord can be downloaded from PyPi. After unpacking the archive, run python setup.py install.

> Installation on a System Without Internet Access
If the system where you want to install Supervisord doesn’t have internet access, you need to perform the installation a bit differently. First, download the dependencies on a machine with internet access:

Copy these files to the target machine and install according to the instructions. This typically means unpacking each file and running python setup.py install.

> Installation from a Distribution Package
Some Linux distributions offer a version of Supervisord that can be installed using the system’s package manager. These packages are created by third parties and often contain changes specific to that distribution.

Configuring Supervisord

After installing Supervisord, you need a configuration file to specify which processes should be managed. Here’s an example configuration file:

[unix_http_server]
file=/tmp/supervisor.sock ; The path to the socket file.
chmod=0700 ; Socket file mode (default 0700).
chown=docker:wheel ; Socket file owner in uid:gid format.
username=docker ; Default is no username (open server).
password=123 ; Default is no password (open server).

[supervisord]
user=root
logfile=/tmp/supervisord.log ; Main log file.
logfile_maxbytes=50MB ; Maximum log file size before rotation.
logfile_backups=10 ; Number of log file backups.
loglevel=info ; Logging level.
pidfile=/tmp/supervisord.pid ; PID file for Supervisord.
nodaemon=false ; Specifies if Supervisord should run in the background.
minfds=1024 ; Minimum available file descriptors at startup.
minprocs=200 ; Minimum available process descriptors.

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; Server URL for the supervisorctl tool.

[program:theprogramname]
command=/bin/cat ; The program to run (relative path uses PATH, can accept arguments).
process_name=%(program_name)s ; Expression for process name (default %(program_name)s).
numprocs=1 ; Number of process copies to start.
directory=/tmp ; Directory in which the process should be run.
umask=022 ; Umask for the process.
priority=999 ; Relative start priority.
autostart=true ; Specifies if the process should start automatically at Supervisord startup.
startsecs=1 ; Number of seconds the program must be running to be considered as started.
startretries=3 ; Maximum number of start attempts.
autorestart=unexpected ; Specifies when to restart if the process exits after running.
exitcodes=0,2 ; 'Expected' exit codes used with autorestart.
stopsignal=QUIT ; Signal used to terminate the process.
stopwaitsecs=10 ; Maximum number of seconds to wait before sending SIGKILL.
stopasgroup=false ; Specifies if the stop signal should be sent to the UNIX process group.
killasgroup=false ; Specifies if the SIGKILL signal should be sent to the UNIX process group.
user=chrism ; UNIX user account under which the program should run.
redirect_stderr=true ; Specifies if the process's standard error should be redirected to standard output.
stdout_logfile=/a/path ; Path to the standard output log file.
stdout_logfile_maxbytes=1MB ; Maximum size of the standard output log file before rotation.
stdout_logfile_backups=10 ; Number of standard output log file backups.
stdout_capture_maxbytes=1MB ; Number of bytes in 'capture mode' for standard output.
stdout_events_enabled=false ; Specifies if events should be emitted on standard output writes.
stderr_logfile=/a/path ; Path to the standard error log file.
stderr_logfile_maxbytes=1MB ; Maximum size of the standard error log file before rotation.
stderr_logfile_backups=10 ; Number of standard error log file backups.
stderr_capture_maxbytes=1MB ; Number of bytes in 'capture mode' for standard error.
stderr_events_enabled=false ; Specifies if events should be emitted on standard error writes.
environment=A="1",B="2" ; Process environment additions.
serverurl=AUTO ; Overrides server URL computation (childutils).

Best practices for Supervisord configuration involve separating the core configuration from that of individual programs and routinely reviewing logs to detect and address issues. A recommended directory structure might look like this:

  • /etc/supervisor/ — the main configuration directory for Supervisord.
  • /etc/supervisor/conf.d/ — directory for individual configuration files for different programs.

Integrating Supervisord with Symfony

In the realm of web development, especially when working with the Symfony framework, efficient process management is paramount. Supervisord emerges as a powerful tool to ensure that our Symfony-based applications run smoothly, with services always available and resilient to failures.

In this section, we’ll focus on what many Symfony developers find crucial: integrating Supervisord to manage various services within a Symfony project. By leveraging Supervisord, we can ensure that essential services like Nginx, PHP-FPM, Symfony Messenger, and others are always up and running, even in the face of unexpected issues.

Let’s explore some of the most commonly used Supervisord configurations in Symfony projects:

— Nginx Configuration

[program:nginx]
command=nginx -g "daemon off;" ; The command to start the service.
priority=5 ; The start priority of the service.
stdout_logfile=/dev/stdout ; Log file for standard output.
stdout_logfile_maxbytes=0 ; Maximum size of the stdout logfile before rotation.
stderr_logfile=/dev/stderr ; Log file for standard error.
stderr_logfile_maxbytes=0 ; Maximum size of the stderr logfile before rotation.
autorestart=true ; Whether to automatically restart the service if it exits.

— PHP-FPM Configuration

[program:php-fpm]
command="php-fpm" ; The command to start the service.
startsecs=10 ; Number of seconds to consider the service as started.
stopwaitsecs=20 ; Maximum number of seconds to wait before forcibly stopping the service.
priority=1 ; The start priority of the service.
stdout_logfile=/dev/stdout ; Log file for standard output.
stdout_logfile_maxbytes=0 ; Maximum size of the stdout logfile before rotation.
stderr_logfile=/dev/stderr ; Log file for standard error.
stderr_logfile_maxbytes=0 ; Maximum size of the stderr logfile before rotation.
autorestart=true ; Whether to automatically restart the service if it exits.

— Symfony Messenger Configuration

[program:messenger]
command=/main/bin/console --no-interaction messenger:consume articles_async comments_async stats_async async --time-limit=86400 ; The command to start the service.
priority=30 ; The start priority of the service.
startsecs=0 ; Number of seconds to consider the service as started.
stopwaitsecs=10 ; Maximum number of seconds to wait before forcibly stopping the service.
numprocs=2 ; Number of process copies to start.
user=www-data ; User to run the service as.
stdout_logfile=/dev/stdout ; Log file for standard output.
stderr_logfile=/dev/stderr ; Log file for standard error.
stdout_logfile_maxbytes=0 ; Maximum size of the stdout logfile before rotation.
stderr_logfile_maxbytes=0 ; Maximum size of the stderr logfile before rotation.
autostart=true ; Whether to start the service when Supervisord starts.
autorestart=true ; Whether to automatically restart the service if it exits.
process_name=%(program_name)s_%(process_num)02d ; Name template for the process.

— Migration Configuration

[program:migrate]
command=/main/bin/console --no-interaction 'doctrine:migrations:migrate' ; The command to start the service.
priority=25 ; The start priority of the service.
startsecs=0 ; Number of seconds to consider the service as started.
user=www-data ; User to run the service as.
stdout_logfile=/dev/stdout ; Log file for standard output.
stderr_logfile=/dev/stderr ; Log file for standard error.
stdout_logfile_maxbytes=0 ; Maximum size of the stdout logfile before rotation.
stderr_logfile_maxbytes=0 ; Maximum size of the stderr logfile before rotation.
autorestart=false ; Whether to automatically restart the service if it exits.

— Cache Configuration

[program:clear-cache]
command=/main/bin/console --no-interaction cache:pool:clear cache.global_clearer ; The command to start the service.
priority=50 ; The start priority of the service.
startsecs=0 ; Number of seconds to consider the service as started.
user=www-data ; User to run the service as.
stdout_logfile=/dev/stdout ; Log file for standard output.
stderr_logfile=/dev/stderr ; Log file for standard error.
stdout_logfile_maxbytes=0 ; Maximum size of the stdout logfile before rotation.
stderr_logfile_maxbytes=0 ; Maximum size of the stderr logfile before rotation.
autorestart=false ; Whether to automatically restart the service if it exits.

— Grafana/Prometheus/DataDog Agent Configuration

[program:grafana-agent]
command=/path/to/grafana-agent --config.file=/path/to/grafana-config.yaml
priority=20
autostart=true
autorestart=true
stdout_logfile=/var/log/grafana-agent.log
stderr_logfile=/var/log/grafana-agent-error.log

[program:prometheus-agent]
command=/path/to/prometheus --config.file=/path/to/prometheus-config.yaml
priority=21
autostart=true
autorestart=true
stdout_logfile=/var/log/prometheus-agent.log
stderr_logfile=/var/log/prometheus-agent-error.log


[program:datadog-agent]
command=/path/to/datadog-agent start
priority=22
autostart=true
autorestart=true
stdout_logfile=/var/log/datadog-agent.log
stderr_logfile=/var/log/datadog-agent-error.log

Conclusion and Further Reading

In this article, we delved deep into the world of Supervisord, exploring its capabilities and how it can be seamlessly integrated with Symfony projects. We’ve covered essential configurations, from setting up Nginx and PHP-FPM to managing Symfony’s Messenger and cache. With the knowledge acquired, managing even large-scale projects becomes a more straightforward and less stressful endeavor.

However, while we’ve touched upon many aspects of Supervisord, there’s always more to learn. The tool’s versatility and depth mean that there are numerous advanced configurations and use cases yet to be explored. For those eager to dive deeper and fully harness the power of Supervisord, I highly recommend referring to the official documentation.

Remember, a well-configured system not only ensures optimal performance but also provides peace of mind. With Supervisord by your side and the insights from this article in your toolkit, you’re well-equipped to tackle any challenges that come your way in the realm of process management.

Happy coding! 🥳

--

--

Jakub Skowron (skowron.dev)

Poland based PHP/Python Web Backend dev. Love to work with Symfony and FastAPI frameworks. In spare time totally gearhead.