django-countdown
Display a maintenance countdown banner across a Django site, then block public access (returning HTTP 503) when the countdown expires. Superusers retain access during maintenance so they can finish the work and clear the countdown.
📖 Full documentation: https://iplweb.github.io/django-countdown/
Why?
Planned downtime is the worst kind of downtime to communicate badly. Users
land on a half-broken page mid-deploy, hit error logs, file support tickets,
and trust erodes. django-countdown lets you announce a maintenance window
before it starts (a countdown banner with a real timer), then during the
window swap public traffic for an explicit "we're in maintenance" page —
while leaving operators unblocked so they can actually finish the work.
Features
- Pre-maintenance banner — an ultra-visible countdown banner inserted into templates via context processor, with a JS timer that ticks live.
- Hard cutoff at expiry — middleware returns HTTP 503 and renders a branded blocked page once the countdown lapses.
- Superuser bypass — admins keep working through the cutoff so they can fix the underlying issue and clear the countdown.
- Maintenance window — optional
maintenance_untillets you set a target end-time; a second banner appears for superusers and the blocked page shows a live countdown to recovery. - Per-Site configuration — uses Django's
sitesframework, so each domain in a multi-tenant setup has its own independent countdown. - A command per verb —
start_countdownschedules a window,show_countdownreports where it stands (with--jsonfor monitoring),extend_countdownandshorten_countdownmove its boundaries when the plan slips, andstop_countdownreopens the site. - Dead man's switch —
extend_countdown --at-least 5mraises a floor rather than adding time, so a deploy loop can hold the site closed while it works and let it reopen by itself if the deploy dies. - Admin integration — full Django admin support alongside the commands.
Supported versions
| Django | 3.10 | 3.11 | 3.12 | 3.13 | 3.14 | Status |
|---|---|---|---|---|---|---|
| 5.2 LTS | ✓ | ✓ | ✓ | ✓ | ✓ | Active LTS (extended support Apr 2028) |
| 6.0 | — | — | ✓ | ✓ | ✓ | Mainstream Aug 2026, extended Apr 2027 |
| 6.1 | — | — | ✓ | ✓ | ✓ | Mainstream Apr 2027, extended Dec 2027 |
All 11 cells are exercised by the CI matrix on every push. Django is the only runtime dependency.
Installation
uv add django-countdown # or: pip install django-countdown
Add the app, the middleware and the context processor to your settings:
INSTALLED_APPS = [
# ...
"django.contrib.sites",
"django_countdown",
]
SITE_ID = 1
MIDDLEWARE = [
# ...
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django_countdown.middleware.CountdownBlockingMiddleware",
]
TEMPLATES = [{
# ...
"OPTIONS": {"context_processors": [
# ...
"django_countdown.context_processors.countdown_context",
]},
}]
Then ./manage.py migrate, and include the banner in your base template:
{% include "django_countdown/countdown_banner.html" %}
Full walkthrough: Installation.
Quick start
./manage.py start_countdown --banner +15m --service +30m \
--message "Database upgrade" --noinput
Banner shows for 15 minutes, then the site returns 503 for 30 minutes, then
reopens by itself. Check on it with ./manage.py show_countdown, and reopen
early with ./manage.py stop_countdown. Use --service indefinite to stay
closed until you do. See
Quickstart.
A working end-to-end example lives under example/.
Documentation
| How it works | The state machine, who sees what, failure behaviour |
| Countdown banner | Including, styling and overriding the banner |
| Blocked page | Three shipped variants and how to write your own |
| Scheduling a countdown | Every option of start_countdown |
| Managing a running countdown | show, stop, extend, shorten, and the deploy patterns |
| Multi-site setup | One countdown per domain |
| Reference | Settings, model, template context, template blocks |
Development
git clone https://github.com/iplweb/django-countdown.git
cd django-countdown
uv sync --all-extras
uv run pytest
See Contributing.
License
MIT — see LICENSE for details.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_countdown-0.3.1.tar.gz.
File metadata
- Download URL: django_countdown-0.3.1.tar.gz
- Upload date:
- Size: 52.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f7cd1762450e8fd16b3418ba19fb66f920a87f123ad9e46ef0434096e53f9037
|
|
| MD5 |
d8c8e4fe22e177a20a1256d73e7f260c
|
|
| BLAKE2b-256 |
cdafa3bc6b4830be4b6acabe31e0f87ae6eb398534650596abe4f16dd0b7d746
|
File details
Details for the file django_countdown-0.3.1-py3-none-any.whl.
File metadata
- Download URL: django_countdown-0.3.1-py3-none-any.whl
- Upload date:
- Size: 48.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50d75d82071f1d12ef27d3a03547c15fbdb0ed6592d8b8e80280f34b22fb4413
|
|
| MD5 |
b6fa27bd3e3fca63e2113731a2132e79
|
|
| BLAKE2b-256 |
597a9e9ba3a78ac0a79eb5ffb56263d8135c1626572b757bc10a7f72db502abf
|