Guide 18
Monitoring Celery with Flower
Audience: Ops and backend engineers running Celery workers on RabbitMQ. Golden rule: Flower is an ops dashboard. Product job status always comes from your `jobs` table / GET /jobs/{id} : never from Flower.
TL;DR
| Topic | Guidance |
|---|---|
| What Flower shows | Workers online, active/reserved/scheduled tasks, basic history, rates |
| What it does not replace | Queue depth/DLQ alerts, app metrics, user-facing job status |
| How to run | celery -A ... flower as a separate process |
| Security | Private network + authentication + TLS (mandatory in prod) |
| Broker | Works with RabbitMQ (this checklist); Redis broker not our job default |
Celery workers ◄── events / inspect ──► Flower (ops UI)
│
└── still emit metrics/logs to Prometheus/your stackContents
- What Flower is for
- What Flower is not for
- Install and run
- Essential configuration
- Security (mandatory)
- What to watch in the UI
- Enable Celery events
- Flower + metrics (better together)
- Deploy sketch
- Troubleshooting
- Checklist
---
1. What Flower is for
Flower is a real-time web monitor for Celery:
- List workers (alive, concurrency, queues)
- See active, reserved, scheduled tasks
- Inspect task success/failure history (when events are enabled)
- Basic rates and task runtime views
- Optional revoke / shutdown controls (treat as dangerous in prod)
Use it during incidents: “Are workers connected? Is a task stuck active? Did failures spike?”
---
2. What Flower is not for
| Not this | Use this instead |
|---|---|
| Customer “is my export done?” | GET /jobs/{id} from application DB |
| Sole source of queue backlog SLOs | RabbitMQ depth / consumer count metrics |
| Public status page | Never expose Flower publicly without auth |
| Long-term analytics warehouse | Prometheus + logs + job table |
| Replace structured logging | JSON logs with job_id / task_id |
See 15 Observability.
---
3. Install and run
# same app env as workers
pip install flower
# PSEUDOCODE : module path to Celery app instance
celery -A app.workers.celery_app.celery_app flower \
--port=5555 \
--basic_auth=ops_user:strong_password# PSEUDOCODE : app/workers/celery_app.py must be importable
from celery import Celery
celery_app = Celery("app", broker=settings.celery_broker_url)Compose / process list:
api | worker | beat (1) | flower (1, private) | rabbitmq | redis | db---
4. Essential configuration
| Flag / setting | Purpose |
|---|---|
--port=5555 | HTTP port (behind internal proxy) |
--basic_auth=user:pass | Built-in basic auth (or put auth at proxy/SSO) |
--broker_api= | Optional RabbitMQ management API URL for more broker insight |
--persistent=True | Persist task state to Flower’s DB (optional; know disk use) |
--db=flower.db | Path when persistent |
--max_tasks=10000 | Cap history size |
--xheaders | Trust proxy headers when TLS terminates upstream |
--url_prefix=flower | If mounted under a path |
# PSEUDOCODE : richer RabbitMQ view (management plugin + credentials)
celery -A app.workers.celery_app.celery_app flower \
--broker_api=https://user:pass@rabbitmq:15672/api/Environment variables (common):
CELERY_BROKER_URL=amqps://...
FLOWER_BASIC_AUTH=ops_user:strong_password
FLOWER_PORT=5555---
5. Security (mandatory)
Never put Flower on the public internet without controls.
| Control | Requirement |
|---|---|
| Network | Private VPC / cluster network / VPN / mesh only |
| Auth | Basic auth or SSO at reverse proxy (OAuth2 proxy, etc.) |
| TLS | Terminate TLS at ingress/proxy |
| Authorization | Ops-only roles; not all developers by default |
| Actions | Disable or restrict revoke/shutdown if your policy requires |
| Secrets | Flower creds ≠ DB creds; rotate |
# PSEUDOCODE : internal reverse proxy sketch
# listen only on internal LB
location /flower/ {
auth_request /oauth2/auth; # or htpasswd
proxy_pass http://flower:5555/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
}Same rules for RabbitMQ Management UI.
---
6. What to watch in the UI
Workers tab
| Signal | Meaning |
|---|---|
| Worker missing | Process crash, deploy, wrong broker URL |
| Concurrency | Prefork pool size vs load |
| Queues | Is worker bound to jobs.io / jobs.heavy? |
Tasks
| State | Meaning |
|---|---|
| Active | Running now |
| Reserved | Prefetched (with prefetch_multiplier=1 this stays small) |
| Succeeded / Failed | Needs task events for reliable history |
During incidents
- Any workers online?
- Active task stuck for too long? (compare to soft_time_limit)
- Failure rate jumping?
- Then check RabbitMQ depth/DLQ and app metrics : not Flower alone
---
7. Enable Celery events
Flower relies on Celery events for live task visibility.
# PSEUDOCODE : celery config (workers)
celery_app.conf.update(
worker_send_task_events=True, # workers emit events
task_send_sent_event=True, # optional: task-sent events
)# workers must not disable events
celery -A app.workers.celery_app.celery_app worker -E -Q jobs.default,jobs.io
# -E is --task-eventsWithout events, Flower may show workers but sparse/empty task history.
---
8. Flower + metrics (better together)
| Need | Flower | Metrics / logs |
|---|---|---|
| “Who is online?” | Excellent | Worker up gauge |
| Queue depth / DLQ | Weak / secondary | RabbitMQ exporter (primary) |
| p95 time-to-complete | Approximate | jobs table + histograms |
| Alerting | Not an alerter | Alertmanager / PagerDuty |
| Trace one job_id | Search if present | Structured logs + OTel |
Minimum production set:
- Flower (private) for humans
- Prometheus metrics: enqueue, success, fail, runtime, depth, consumers
- Alerts: zero consumers, DLQ > 0, success drop, SLO breach
- JSON logs with
job_id+task_id
---
9. Deploy sketch
# PSEUDOCODE : docker-compose fragment
services:
flower:
image: your-app:${TAG}
command: >
celery -A app.workers.celery_app.celery_app flower
--port=5555
--basic_auth=${FLOWER_BASIC_AUTH}
environment:
CELERY_BROKER_URL: ${CELERY_BROKER_URL}
# no public ports in production : only attach to internal network
networks: [internal]
depends_on: [rabbitmq]
restart: unless-stoppedKubernetes:
Deploymentreplicas: 1 is enough for FlowerServiceClusterIP only- Ingress with auth + TLS, or no Ingress (port-forward / VPN)
- Resource limits modest (CPU/memory grow with
--persistentand task volume)
---
10. Troubleshooting
| Symptom | Checks |
|---|---|
| Empty workers | Broker URL; workers running; same vhost; network policy |
| Empty tasks | -E / worker_send_task_events; clock skew |
| Flower OOM | Lower --max_tasks; disable or bound persistence |
| Stale state | Restart Flower; check broker connectivity |
| Auth loops | url_prefix, proxy headers, cookie paths |
| “It works in Flower but user status wrong” | You’re using Flower as product truth : fix API/DB |
---
11. Checklist
- [ ] Flower runs as its own process/container (not inside API)
- [ ] Same Celery app module / broker as workers
- [ ] Task events enabled on workers (
-E/ config) - [ ] Private network only
- [ ] Authentication enabled (basic or SSO)
- [ ] TLS at the edge
- [ ] Documented: Flower ≠ user job status
- [ ] RabbitMQ depth/DLQ/consumer metrics + alerts still configured
- [ ] Optional
--broker_apionly over TLS with locked-down creds - [ ] Revoke/admin actions limited by policy
- [ ] Runbook link from dashboard (scale workers, redrive DLQ)
---
Quick commands
# start
celery -A app.workers.celery_app.celery_app flower --port=5555 --basic_auth=user:pass
# worker with events
celery -A app.workers.celery_app.celery_app worker -E -Q jobs.default,jobs.io -c 4
# never: expose 5555 on 0.0.0.0 to the internet without auth---
Prometheus: 19 Integrate Flower with Prometheus
See also
- 15 Observability, metrics, and Flower : health, SLOs, alerts
- 05 Celery + RabbitMQ
- 14 Celery Beat
- 17 Celery RL + lock pipeline
- 09 Errors / DLQ