Workers, Cron and Private Networking
Background workers
Railway: add another service from the same repository with a different start command (rq worker --url $REDIS_URL), sharing variables.
Fly.io: define process groups in fly.toml:
[processes]
app = "gunicorn wsgi:app -c gunicorn.conf.py"
worker = "rq worker --url $REDIS_URL default"
[http_service]
internal_port = 8080
processes = ["app"] # only the app group receives HTTP
fly scale count app=2 worker=1
Scheduled jobs
- Railway: set a cron schedule on a service so it runs a command on a timetable and exits.
- Fly.io: run a small scheduler process or use scheduled machines; check current Fly docs for the recommended pattern.
Either way, make jobs idempotent, log their results and use a heartbeat monitor to detect missed runs.
Private networking
Services in the same Railway project, and apps in the same Fly organisation, can talk over a private network using internal hostnames (Fly uses .internal DNS names). Keep internal services (admin APIs, workers' dashboards) off the public internet entirely.
Graceful shutdown
Deploys stop old processes. Workers should finish (or safely re-queue) the current job when they receive a termination signal, so an email is never sent twice or lost mid-deploy. Queue libraries such as RQ and Celery handle this when configured with sensible timeouts.
Exercise
Add a worker that processes queued emails and a nightly cleanup job on your chosen platform; expose neither publicly, and verify the web app reaches the worker's Redis over the private network.
Check yourself
- How does Fly route HTTP only to the app process group?
- Why should scheduled jobs be idempotent?
- What should never be exposed publicly?
Your progress is saved in this browser only.
Submit for review (10 points)