Supervisor (supervisord) turns any long-running command into a managed background service (daemon) with a few lines of INI config — no init scripts required. You install it, drop a [program:x] block into /etc/supervisor/conf.d/, then run supervisorctl reread && supervisorctl update to start supervising the process, with automatic restarts if it crashes.
Daemonizing a command means running it as a persistent background process that detaches from your shell, starts on boot, and comes back if it dies. The old approaches — upstart, appending to /etc/rc.local, or hand-writing /etc/init.d scripts and wiring up runlevel symlinks — are fiddly and largely deprecated. Supervisor replaces all of that with one readable config file per process and a single control CLI (supervisorctl).
One honest caveat up front: on modern Linux, systemd is the default init and service manager, and for most system-level services it is the better choice. Supervisor still earns its place in specific situations, which the comparison table below spells out.
Key takeaways
- Supervisor daemonizes any command via a per-process
[program:x]INI block in/etc/supervisor/conf.d/*.conf. - The core workflow is
supervisorctl reread→supervisorctl updateafter editing config, thenstatus,restart, andstopto operate it. - Essential directives:
command,directory,user,autostart,autorestart,startretries,stdout_logfile,stderr_logfile, andenvironment. - Use
[group:x]to start and stop several related processes together, andnumprocsto run multiple instances of one program. - On modern Linux, systemd is the default service manager; reach for Supervisor when you need non-root user-space supervision, simple grouped processes, or process control inside a container with no init.
How do you install Supervisor?
On Debian and Ubuntu, install it from apt:
sudo apt-get update
sudo apt-get install -y supervisorOn RHEL, Rocky, or AlmaLinux it lives in EPEL:
sudo dnf install -y epel-release
sudo dnf install -y supervisorSupervisor is also a plain Python package (pip install supervisor), but the distro packages do extra work for you: they create /etc/supervisor/ and register supervisord as a boot service. Ironically, that boot service is itself managed by systemd:
sudo systemctl enable --now supervisor # supervisord starts on boot
systemctl status supervisorWhere do Supervisor configs live?
The main file is /etc/supervisor/supervisord.conf. Near its end it includes a directory glob:
[include]
files = /etc/supervisor/conf.d/*.conf
So you never edit the main file for app processes — you drop one .conf per program into /etc/supervisor/conf.d/. That keeps each service self-contained and easy to add, remove, or copy between hosts.
How do you write a [program] config block?
A program block names the command and tells Supervisor how to run, log, and restart it. Create /etc/supervisor/conf.d/myapp.conf:
; /etc/supervisor/conf.d/myapp.conf
[program:myapp]
command=/usr/bin/python3 /srv/myapp/worker.py ; command to daemonize (absolute path)
directory=/srv/myapp ; cwd set before the command runs
user=appuser ; drop privileges to this user
autostart=true ; start when supervisord starts
autorestart=true ; restart on unexpected exit
startretries=3 ; give up (FATAL) after N failed starts
startsecs=5 ; must stay up this long to count as RUNNING
stopwaitsecs=10 ; grace period before SIGKILL
stdout_logfile=/var/log/myapp/out.log
stderr_logfile=/var/log/myapp/err.log
stdout_logfile_maxbytes=50MB ; rotate at this size
stdout_logfile_backups=10 ; keep this many rotated files
environment=ENV="production",PORT="8000" ; injected into the process environmentKey [program] directives
| Directive | What it does |
|---|---|
command |
Exact command to run; use absolute paths, no shell features |
directory |
Working directory set before the command starts |
user |
Run as this unprivileged user instead of root |
autostart |
Start automatically when supervisord boots (true/false) |
autorestart |
true, false, or unexpected — the restart policy on exit |
startretries |
Restart attempts before the program is marked FATAL |
startsecs |
Seconds the process must run to count as successfully started |
stdout_logfile / stderr_logfile |
Files where Supervisor captures the process output |
environment |
Comma-separated KEY="value" pairs added to the process env |
How do you apply config changes?
After adding or editing any file in conf.d/, tell Supervisor to pick it up:
sudo supervisorctl reread # parse config files, report added/changed/removed
sudo supervisorctl update # apply: start new programs, restart changed ones
sudo supervisorctl status # show each program's state and PIDreread only detects changes and prints what it found; it does not touch running processes. update applies them — starting newly added programs, stopping removed ones, and restarting any whose config changed. status reports each program as RUNNING, STOPPED, STARTING, BACKOFF, or FATAL along with its PID and uptime.
How do you start, stop, and restart processes?
Once a program is defined, control it by name (the original version of this post had a copy-paste slip here — start uses start, not stop):
sudo supervisorctl start myapp
sudo supervisorctl stop myapp
sudo supervisorctl restart myapp
sudo supervisorctl stop all # every program
sudo supervisorctl restart all
sudo supervisorctl tail -f myapp stderr # live-follow a program's log
sudo supervisorctl # drop into the interactive shellHow do you manage multiple processes as a group?
Define each process as its own [program:x] block, then bind them with a [group:x] block so you can control them as a unit. numprocs runs several identical copies of one program:
[program:worker]
command=/srv/app/venv/bin/celery -A app worker
numprocs=4 ; run 4 identical workers
process_name=%(program_name)s_%(process_num)02d
autorestart=true
[program:beat]
command=/srv/app/venv/bin/celery -A app beat
autorestart=true
[group:celery]
programs=worker,beat ; control both togetherNow supervisorctl restart celery:* restarts every member of the group, and supervisorctl status celery:* reports them together. The numprocs + process_name combination spins up copies named worker_00, worker_01, and so on. If you are running Celery specifically, see our deeper walkthrough on running Celery workers with Supervisor.
What is an [eventlistener]?
An [eventlistener] is a special program that subscribes to Supervisor's event stream (PROCESS_STATE, TICK, and others) instead of being a plain daemon. It receives events on stdin and can react — for example, emailing you when a process enters FATAL, or restarting a process that exceeds a memory threshold. The superlance package ships ready-made listeners like crashmail and memmon:
[eventlistener:crashmail]
command=crashmail -a -m admin@example.com
events=PROCESS_STATE_EXITEDSupervisor vs systemd: which should you use?
On modern Linux distributions, systemd is the default init system and service manager. You write a unit file in /etc/systemd/system/myapp.service and enable it with systemctl enable --now myapp. For most boot-time, system-level services this is the right default — systemd is already PID 1, integrates with journald logging, cgroups, sockets, and unit dependencies.
| Concern | Supervisor | systemd |
|---|---|---|
| Requires root | Yes for a system install (or run a user instance) | Yes for system units; user units via systemctl --user |
| Per-user services | Possible but manual | First-class (systemctl --user, lingering) |
| Inside containers | Great — a tiny init when the base image has none | Heavy; usually avoided in single-process containers |
| Process grouping | [group:x] controls many programs at once |
Targets and .slice units, more verbose |
| Multiple instances | numprocs + process_name |
Template units (myapp@.service) |
| Log handling | Writes to stdout_logfile / stderr_logfile |
journald (journalctl -u myapp) plus optional files |
| Auto-restart | autorestart, startretries |
Restart=, StartLimitBurst= |
| Boot integration | Itself started by systemd/init | Native PID 1 |
| Config style | One INI file per program | One unit file per service |
The systemd equivalent of the program above is short:
# /etc/systemd/system/myapp.service
[Unit]
Description=My app worker
After=network.target
[Service]
ExecStart=/usr/bin/python3 /srv/myapp/worker.py
WorkingDirectory=/srv/myapp
User=appuser
Restart=on-failure
Environment=ENV=production PORT=8000
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now myapp
sudo systemctl status myappUse systemd when the host is a normal systemd Linux box and you want a service to start at boot, log to journald, and depend on other units — that is the modern default.
Use Supervisor when:
- You are a non-root user who needs to supervise processes in user space without touching system units.
- You want to manage many related processes together with one concise, portable INI file.
- You run inside a container whose base image has no init and you need to keep one or two processes alive.
- You want a single, distro-agnostic config that behaves identically on Ubuntu, Debian, and older hosts.
For a typical app worker on a modern VM, a systemd unit is the cleaner choice. Supervisor shines for user-space, grouped, or container scenarios — and for teams who simply prefer its INI format and supervisorctl ergonomics.
Tips for production
- Always use absolute paths in
commandanddirectory. Supervisor does not run your shell, so~,$VARS, pipes, and redirects are not expanded — wrap them in a script orbash -c '...'when you need them. - Set
user=so workers never run as root. - Watch
supervisorctl statusforFATAL(exited too many times) andBACKOFF(restart loop) states. - Roll config out across a fleet with Ansible for server process automation, and pipe application errors into Sentry for error and performance monitoring so a crash-looping process surfaces immediately.
- Keeping production services healthy at scale is exactly what our server maintenance and DevOps services cover.
Frequently Asked Questions
What does it mean to daemonize a command?
Daemonizing means running a command as a persistent background process (a daemon) that is detached from your terminal, survives logout, starts on boot, and restarts automatically if it crashes. Supervisor does this by launching your command under its own supervisord parent process and keeping it alive according to the autostart and autorestart rules in your config.
How do I daemonize a command with Supervisor?
Install supervisor, create /etc/supervisor/conf.d/myapp.conf with a [program:myapp] block that sets command, user, autostart=true, autorestart=true, and log files, then run sudo supervisorctl reread followed by sudo supervisorctl update. Supervisor starts the process immediately and re-launches it whenever it exits unexpectedly.
What is the difference between reread and update in supervisorctl?
supervisorctl reread re-parses the configuration files and reports what was added, changed, or removed, but it does not act on running processes. supervisorctl update applies those changes: it starts newly added programs, stops removed ones, and restarts programs whose config changed. Run reread then update after every config edit.
Should I use Supervisor or systemd?
On a modern Linux host, systemd is the default init system and is usually the better choice for boot-time, system-level services — it logs to journald and resolves dependencies natively. Choose Supervisor when you need user-space supervision without root, want to control many related processes with one portable INI file, or are running inside a container that has no init system.
How do I manage multiple processes together in Supervisor?
Define each process as its own [program:x] block, then add a [group:name] block with programs=a,b,c. You can then act on all of them at once with commands like supervisorctl restart name:* and supervisorctl status name:*. To run several copies of one program, set numprocs together with a unique process_name template.
Why does my Supervisor program keep showing FATAL or BACKOFF?
BACKOFF means the process is failing to stay up for startsecs and Supervisor is retrying; FATAL means it exhausted startretries and gave up. Common causes are a wrong command path, a missing directory, the wrong user, or unset environment variables. Check the stderr_logfile, confirm every path is absolute, and remember Supervisor does not expand shell syntax — wrap pipes or variables in bash -c.