# RedBeat: a Celery Beat scheduler whose schedule lives in Redis

> RedBeat replaces the local schedule file that Celery Beat keeps on one machine with a Redis backed store, so the set of periodic tasks can be created and changed while the scheduler is running, from any language with a Redis client. It suits a deployment where the schedule is managed programmatically or by more than one service, and it depends on a Redis or Valkey server being reachable from the scheduler.

**sibson/redbeat** — RedBeat is a Celery Beat Scheduler that stores the scheduled tasks and runtime metadata in Redis.

- Repository: https://github.com/sibson/redbeat
- Stars: 1,054 · Forks: 147
- Language: Python
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/sibson-redbeat

## What RedBeat changes

RedBeat is a Celery Beat Scheduler that stores the scheduled tasks and runtime metadata in Redis, and it replaces the stock scheduler rather than wrapping it in a new concept.

The stock Celery Beat keeps its schedule in a local file, which the scheduler reads when it starts. That has two consequences that are the whole reason this project exists. The schedule is only as fresh as the last restart, and it is tied to the machine that holds the file.

The first is the problem the README calls dynamic live task creation and modification, without lengthy downtime. A scheduler that reads its schedule from a shared store can be told about a new periodic task while it is running, and the next tick picks it up. No restart, no downtime, and the change is atomic because it is a write to a key.

The second is that the schedule is no longer private to one process. Because the store is Redis, anything with a Redis client can read and write it, and the README lists that as externally manage tasks from any language with Redis bindings.

The rest of the design follows from the same store. The schedule is shared data, so Beat is not tied to a single drive or machine, which means two schedulers can look at the same schedule. That creates a problem the project solves with a lock, which the README lists as preventing accidentally running multiple Beat servers.

The project is Apache-2.0 licensed, by Marc Sibson, and the current version in the manifest is a development build of 2.4.3, with 2.4.2 released in July 2026 and the last push to the repository on 2026-10-09.

## The five reasons, and the shared store underneath them

The Why RedBeat section lists five reasons, and they are worth reading as a set because they describe one design decision from five angles.

The first is dynamic live task creation and modification, without lengthy downtime, which is the capability the Redis store buys.

The second is externally managing tasks from any language with Redis bindings. This is the one that changes who can be in charge of a schedule. If a deployment platform, a management console or a deployment pipeline decides what should run and when, and it can speak Redis, then it does not need to speak Celery or Python at all. The schedule stops being configuration inside an application and becomes data with an owner.

The third is a shared data store, so Beat is not tied to a single drive or machine. The fourth is fast startup even with a large task count, which follows from the same place, since a scheduler reading its tasks from a key value store does not have to parse and load a schedule file.

And the fifth is preventing accidentally running multiple Beat servers.

Read together, they describe a scheduler whose state is external, shared, and therefore writable at run time. The trade is the obvious one, which the project states plainly, and it is worth noticing that the second reason is the one that is impossible with a local file. A file on one disk cannot be edited safely by a service on another machine, and even on the same machine the edit needs to reach the running scheduler.

## Configuration and running it

There are three pieces to set up, and they are all small.

The install is from PyPI.

```bash
pip install celery-redbeat
```

Then a single setting in the Celery configuration file, pointing at the Redis database that will hold the schedule.

```python
redbeat_redis_url = "redis://localhost:6379/1"
```

And then the scheduler is selected on the command line when Beat runs.

```bash
celery beat -A <celery_app_file_path>.<celery_app_instance_name> -S redbeat.RedBeatScheduler
```

If the Beat is embedded in a worker process, as is common in development, the scheduler is named the same way.

```bash
celery worker --beat --scheduler redbeat.RedBeatScheduler ...
```

The -S flag is the standard Celery way of choosing a scheduler implementation, so RedBeat slots in wherever the built in one would go, and the only new configuration is the Redis URL.

There is a sample configuration in the repository, an example configuration file, and the README shows how to start a sample Beat instance with it, which is a quick way to see the scheduler running without wiring it into a real application.

## The lock, and the decision to disable it

RedBeat uses a distributed lock to prevent multiple instances running, and there is a documented setting to turn that off.

```python
redbeat_lock_key = None
```

The default behaviour is that a second Beat instance will not start while another holds the lock, so a deployment that accidentally runs two schedulers does not fire every periodic task twice. That is a genuine failure mode in containerised deployments, where a rolling update briefly has two pods running, and in the case of a misconfigured process supervisor.

The setting exists because there are situations where the lock is the wrong behaviour. If your schedulers are deliberately partitioned, if the lock key is in a Redis instance that is not shared with every scheduler, or if you are running a development instance against a production store, the lock will block a process you wanted to run.

Setting the key to None disables the protection entirely, so the decision is explicit rather than accidental, and that is the right way to expose a switch like this.

The interesting detail is what the lock is protecting. It is not a lock on the schedule data, it is a lock on the scheduler process, because the schedule itself is deliberately shared. Two Beat instances reading the same Redis store agree on what to run, and the lock is what stops them from both running it.

## Redis and Valkey, and the single supported client

The server compatibility section is short and it is a commitment rather than an observation.

RedBeat works with both Redis and Valkey servers, and you point the Redis URL setting at either one. The intent is to maintain support for both for as long as neither introduces a major behaviour change that RedBeat depends on, or an incompatibility upstream.

The reason that matters is the client. RedBeat uses the redis-py client to talk to either server, and it is the only supported client library.

That is a deliberate narrowing, and the reason is that a scheduling store needs a specific set of semantics, most importantly the atomic operations that let a lock work across processes. Supporting several clients would mean supporting several sets of semantics, and the project has chosen to support several servers on one client instead.

Valkey is a fork of Redis, so a client that speaks the Redis protocol speaks to it, and the interesting question is whether the commands RedBeat needs, the ones for locking and for the atomic schedule updates, behave identically. That is the compatibility question the maintenance commitment is about, and the wording of the promise, neither side introducing a major behaviour change or an upstream incompatibility, is an honest description of what can be guaranteed by a project that does not control either server.

## The development setup and the test approach

The development section describes three ways to work on the project, and they are all current tooling rather than the older virtualenv and setup.py approach.

The recommended one is make, with a setup target and a test target.

```bash
make setup
make test
```

The second is uv directly, which syncs the environment and runs the standard library test discovery.

```bash
uv sync
uv run python -m unittest discover tests
```

The third is for people who manage Python with mise. It reads the tool configuration file in the repository root and installs the pinned Python version, and then you still use make for the tasks.

```bash
mise install
make setup && make test
```

The choice of the standard library test runner rather than pytest is notable, and it tells you the test suite does not need a plugin framework. The development dependencies do include pytest and fakeredis, so the two coexist, and fakeredis with the Lua extra is the key one, since it lets the test suite run an in-process fake of the server with the scripting commands that the locking depends on.

The formatting and linting setup is a Black configuration with a line length of 100, a target of Python 3.9 and string normalisation turned off, paired with flake8, flake8-black and flake8-isort so the two tools agree, and an isort profile named hug. The package is built with hatchling, and the distribution includes the package, the tests, the docs and the example configuration file.

## The state of the project and the version numbering

Two details about how the project is released are worth knowing before you pin a version.

The first is that the manifest version is a development build, 2.4.3.dev0, while the latest tagged release is 2.4.2. So the default branch is ahead of the last release, which is the normal arrangement for a project that releases from a development branch, and it means the version you get from a source install is not the version you get from PyPI.

The second is the release cadence, with 2.4.2 in July 2026, 2.4.1 in July and 2.4.0 in March, and the last push in October 2026. That is a project in maintenance with a stable line rather than one in rapid development, which suits a scheduler that people put into production and then leave alone.

The package metadata is worth a glance for the deployment side. The Python requirement is 3.9 or later and the classifiers run from 3.9 to 3.13, the runtime dependencies are four, with the Redis client, Celery itself, a date parser and the retry library, and the package is classified as Production/Stable and OS Independent.

That last classification is a reasonable summary of the project. It is a small, focused replacement for one component of a well known scheduler, it depends on infrastructure you almost certainly already run if you run Celery, and it trades a little of the local simplicity of the stock Beat for a schedule that can be managed from outside the process.

## Conclusion

Adopt RedBeat if you need to change periodic tasks without restarting the scheduler, or if more than one service manages the schedule, because a Redis backed store is what makes that possible and the shipped lock is what keeps two schedulers from firing the same task twice. Do not adopt it if a single process with a static schedule is enough, since that is a case Celery Beat already handles. Before you deploy, decide whether you want the lock, because the distributed lock is on by default and disabling it removes the protection against two Beat servers running at once.

## FAQ

### What is RedBeat?

It is a Celery Beat Scheduler that stores the scheduled tasks and runtime metadata in Redis, so the set of periodic tasks can be created and modified while the scheduler is running, and the schedule is a shared data store rather than a local file tied to one machine.

### How do I install and configure RedBeat?

Install it with pip install celery-redbeat, set redbeat_redis_url in your Celery configuration file, for example to a local database on port 6379, and then select the scheduler when running Beat with the -S flag pointing at redbeat.RedBeatScheduler. For an embedded beat in a worker, the same scheduler is named with the --scheduler option.

### How does RedBeat stop two schedulers from running at once?

It uses a distributed lock, so a second Beat instance will not start while another holds it. The protection can be turned off by setting redbeat_lock_key to None, which the documentation offers for the cases where the lock is in the way, such as a development instance or a deliberately partitioned setup.

### Does RedBeat work with Valkey as well as Redis?

Yes. You point the redbeat_redis_url setting at either server, and the intention is to maintain support for both for as long as neither introduces a major behaviour change that RedBeat depends on or an incompatibility upstream. RedBeat talks to both through the redis-py client, which is the only supported client library.

## Sources

- [Issues](https://github.com/sibson/redbeat/issues)
- [License: Apache-2.0](https://github.com/sibson/redbeat/blob/main/LICENSE)
- [README](https://github.com/sibson/redbeat/blob/main/README.md)
- [Releases](https://github.com/sibson/redbeat/releases)
- [sibson/redbeat on GitHub](https://github.com/sibson/redbeat)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/sibson-redbeat
