Supervisor is a lightweight process manager that watches non-daemonized programs, restarts them when they die, and gives you a simple CLI (supervisorctl) to start, stop, reload, and tail them. On a Rocky Linux 10 server it pairs naturally with Celery — the Python distributed task queue — because Celery workers are exactly the kind of long-running foreground processes Supervisor was built for. If you prefer systemd units to manage processes, see our systemctl reference for that alternative.
This guide installs Supervisor and Celery on Rocky Linux 10, stands up Valkey as the message broker (Valkey is the open source Redis replacement now shipping in Rocky 10 AppStream), defines a Supervisor program block for a Celery worker, and verifies the worker actually registers tasks and runs.
Tested April 2026 on Rocky Linux 10.1 with Supervisor 4.2.5, Celery 5.6.3, and Valkey 8.0.7
Step 1: Install Supervisor
Supervisor is in the Rocky Linux 10 AppStream repository, so a single DNF call installs both the daemon and the Python pip tool we need for Celery in the next step:
sudo dnf install -y supervisor python3-pip
sudo systemctl enable --now supervisord
Check the daemon is active and note the version:
systemctl is-active supervisord
supervisord --version
Rocky 10.1 currently ships supervisor 4.2.5, which is the current upstream stable:
active
4.2.5
Step 2: Install Valkey as the broker
Rocky 10 removed the Redis package in favour of Valkey, the Linux Foundation fork that replaced Redis after the license change. Valkey is wire-compatible with the Redis protocol so everything that talks Redis (including Celery’s redis:// URL scheme) works against it unchanged:
sudo dnf install -y valkey
sudo systemctl enable --now valkey
valkey-cli ping
A healthy Valkey server responds with PONG:
PONG
Step 3: Install Celery
Celery lives on PyPI. You need the redis Python client too so Celery’s kombu transport can talk to Valkey:
sudo pip3 install celery redis
Confirm Celery is installed and note the version:
python3 -c 'import celery; print(celery.__version__)'
Current Celery stable at the time of testing:
5.6.3
For production, create a virtualenv under the app’s home directory and install Celery there instead of the system Python. The pip3 system install shown here is fine for a quick working setup.
Step 4: Write a tiny task module
Create a Python file that defines one Celery task so there’s something for the worker to load:
sudo useradd -r -m -s /sbin/nologin celery
sudo mkdir -p /var/log/celery
sudo chown celery:celery /var/log/celery
sudo tee /home/celery/tasks.py >/dev/null <<'EOF'
from celery import Celery
app = Celery(
"tasks",
broker="redis://localhost:6379/0",
backend="redis://localhost:6379/0",
)
@app.task
def add(x, y):
return x + y
EOF
sudo chown celery:celery /home/celery/tasks.py
The broker and backend URLs both use the redis:// scheme because Valkey speaks the Redis protocol. Celery doesn’t need to know it’s actually talking to Valkey.
Step 5: Define the Supervisor program
Drop a new config file under /etc/supervisord.d/. Supervisor scans that directory for *.ini files automatically:
sudo tee /etc/supervisord.d/celery.ini >/dev/null <<'EOF'
[program:celery-worker]
command=/usr/local/bin/celery -A tasks worker --loglevel=info
directory=/home/celery
user=celery
numprocs=1
autostart=true
autorestart=true
stdout_logfile=/var/log/celery/worker.log
stderr_logfile=/var/log/celery/worker.err.log
EOF
Key options: directory= is where Celery looks for the tasks module, user= drops privileges to the dedicated service account, and autorestart=true makes Supervisor restart the worker if it crashes. Tell Supervisor to reread the config and pick up the new program:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl status
The worker should come back as RUNNING with a PID and uptime:
celery-worker RUNNING pid 6205, uptime 0:00:04
Step 6: Verify the worker loaded the task
Tail the worker log to see the Celery banner and registered tasks:
sudo tail -15 /var/log/celery/worker.log
You should see the Celery banner block listing the app name, transport, results backend, concurrency, and the loaded task list:
- *** --- * ---
- ** ---------- [config]
- ** ---------- .> app: tasks:0x7f77d3a2fd10
- ** ---------- .> transport: redis://localhost:6379/0
- ** ---------- .> results: redis://localhost:6379/0
- *** --- * --- .> concurrency: 1 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
-------------- [queues]
.> celery exchange=celery(direct) key=celery
[tasks]
. tasks.add
The [tasks] section confirms the add task from tasks.py loaded correctly. From here, any Python process that imports the same Celery app and calls add.delay(2, 3) will enqueue a job on Valkey, the worker will pick it up, and the result (5) will be written back to the Valkey backend.
Step 7: supervisorctl cheat sheet
sudo supervisorctl status— list all programs and their statesudo supervisorctl start celery-worker— start a stopped programsudo supervisorctl stop celery-worker— stop a running programsudo supervisorctl restart celery-worker— restart after a code changesudo supervisorctl tail celery-worker— tail its stdout logsudo supervisorctl tail celery-worker stderr— tail its stderr logsudo supervisorctl update— reload config and start any new programs
Firewall note
Supervisor and Celery don’t open any external ports. Valkey binds to localhost by default so it’s not exposed either. If you move Valkey to a dedicated host, make sure to bind it to an internal interface and allow the Celery worker host through the firewall. Our Rocky Linux 10 post-install tips cover the firewalld install that’s a prerequisite for any service exposure.
Wrap up
Supervisor + Celery + Valkey is a classic three-part task queue for Python apps that don’t need the weight of a Kubernetes deployment. All three packages come from official Rocky 10 AppStream or PyPI, no third-party repos required. For related monitoring, see our Prometheus MySQL exporter guide if your workers are hammering a database, and our ncdu disk usage analyzer for tracking log file growth. If you swap Valkey for a dedicated RabbitMQ broker, the Celery config changes to amqp:// but everything else in this guide stays the same.