Installation - ptweezy/cronstable GitHub Wiki
Install cronstable with a package manager, a standalone binary, or the published container image. This page covers requirements and setup for each method. For Windows-specific behavior, see running on Windows.
| Requirement | Value |
|---|---|
| Python (pip/pipx) |
>= 3.10 (requires-python = ">=3.10"). Versions 3.10, 3.11, 3.12, 3.13, 3.14, and 3.15 are supported and tested. For an older Python, use the standalone binary instead. |
| Operating system | Linux, macOS, and Windows. cronstable/platform.py isolates OS-specific behavior. grp and pwd are imported only on POSIX. A few features differ on Windows; see running on Windows. |
| CPU architectures | Linux: amd64 (x86_64), arm64, i686 (32-bit x86), armv7 (32-bit ARM), ppc64le (POWER) and s390x (IBM Z), for both the container image and the prebuilt binaries. The prebuilt binaries also cover armv6 and riscv64 and loong64 (LoongArch) in both libcs, plus mips64le and armel (glibc). macOS: amd64 and arm64. Windows: amd64 (x64), arm64 (ARM64) and i686. Also FreeBSD (amd64, arm64), OpenBSD, NetBSD and illumos (amd64). Every amd64 release binary/package and Docker distro has an amd64v3 counterpart, recommended for compatible CPUs; amd64 is the compatibility build; see CPU requirements. |
Python is required only for the pip/pipx installs. The container image
bundles its own interpreter, and the standalone binaries embed Python, so
neither needs Python on the target host.
Installing the cronstable distribution pulls in the following, taken from
pyproject.toml:
| Dependency | Version constraint |
|---|---|
strictyaml |
>=1.7.3,<2 |
aiohttp |
>=3.14.4,<4 |
sentry-sdk |
>=2.8.0,<3 |
aiosmtplib |
>=5.1.3,<6 |
jinja2 |
>=3.1.6,<4 |
tzdata |
>=2024.1 |
psutil |
>=5.9.0 |
Each minimum is the oldest release that cronstable supports, so you can install cronstable next to packages that need an older release of a shared dependency.
tzdata ships the IANA time-zone database so zoneinfo resolves time zones on
minimal/slim images that do not include the system tz data. See
schedules and time zones.
| Method | Source | Embeds Python? | Self-extracts at startup? |
|---|---|---|---|
| Container image | ghcr.io/ptweezy/cronstable |
Yes (in-image interpreter) | No |
| pip | PyPI (cronstable) |
No (uses your interpreter) | No |
| pipx | PyPI (cronstable) |
No (uses your interpreter) | No |
| Standalone binary | GitHub Releases | Yes (embedded) | Yes |
| Windows zip (one-directory) | GitHub Releases | Yes (embedded) | No |
| Windows MSI | GitHub Releases | Yes (embedded) | No |
| Homebrew | cronstable tap (release binary) | Yes (embedded) | Yes |
| winget | winget-pkgs (signed setup that installs the MSI) | Yes (embedded) | No |
| Scoop | Manifest attached to each release (release binary) | Yes (embedded) | Yes |
.deb / .rpm
|
GitHub Releases | Yes (embedded) | Yes |
.apk (Alpine) |
GitHub Releases | Yes (embedded) | Yes |
.pkg (FreeBSD) |
GitHub Releases | Yes (embedded) | Yes |
ubi / mise
|
GitHub Releases | Yes (embedded) | Yes |
| Nix | this flake | No (uses nixpkgs Python) | No |
Only the standalone binary, including the copies Homebrew and Scoop install,
self-extracts at startup and therefore needs a writable and executable temp
directory (see
standalone binary temp-directory requirement).
The image, the pip/pipx installs, and the Windows zip and MSI (whose
files sit on disk beside cronstable.exe) run without self-extracting.
Prebuilt, multi-architecture (linux/amd64, linux/arm64, linux/386,
linux/arm/v7, linux/ppc64le, linux/s390x and linux/riscv64) images are
published on every release to two registries: the GitHub Container Registry
(ghcr.io/ptweezy/cronstable) and Docker Hub (docker.io/ptweezy/cronstable).
The images are identical, so pull from whichever you prefer. Mount your crontab
and run:
docker run --rm \
-v "$PWD/cronstable.yaml:/etc/cronstable.d/cronstable.yaml:ro" \
ghcr.io/ptweezy/cronstable:latestFor an x86-64-v3-capable host, latest-amd64v3 is recommended; substitute
it for latest in the command above. Tags without the -amd64v3 suffix use the
baseline amd64 build on x64. See variant selection
before choosing a v3 image, including inside a VM.
The image runs as the non-root user 65534:65534. Its entrypoint is
cronstable with default arguments -c /etc/cronstable.d, so it reads
configuration from /etc/cronstable.d unless you override the arguments. For
production, pin a specific version instead of latest (for example,
ghcr.io/ptweezy/cronstable:1.2.60).
To include your configuration in your own image, base it on the published image:
FROM ghcr.io/ptweezy/cronstable:latest
# The base image already runs as the non-root user 65534.
COPY cronstable.yaml /etc/cronstable.d/cronstable.yamlThe image is built from python:3.14-slim (a multi-stage build that copies a
self-contained venv into the runtime stage) and sets PYTHONUNBUFFERED=1 and
PYTHONDONTWRITEBYTECODE=1. It requires no writable paths at runtime. For the
hardened Kubernetes/Docker setup (read-only root filesystem, dropped
capabilities, fsGroup), see
production and container deployment.
The amd64, amd64v3, and arm64 images also include uvloop and isal from
the speedups extra. The other architectures have no wheels for them, so
those images run on stock asyncio and gzip responses with the standard
library's zlib.
The default latest (and <version>) image is built on Debian (slim). The
same release is also published on several other bases, so you can match a
specific one to your environment: a familiar userland, an image-provenance
policy that mandates a particular vendor, or the smallest possible image. Each
variant adds a -<distro> suffix to the tag, and the default Debian image is
also available explicitly as -debian:
| Tag suffix | Base image | Python | Notes |
|---|---|---|---|
(none) / -debian
|
python:3.14-slim |
3.14 | Default. Widest architecture coverage. |
-alpine |
python:3.14-alpine |
3.14 | musl libc; smallest image. |
-ubuntu |
ubuntu:26.04 |
3.14 | Ubuntu LTS userland. |
-rhel |
UBI 10 (ubi-minimal) |
3.12 | Red Hat base for RHEL / OpenShift. |
-fedora |
fedora:44 |
3.14 | Leading-edge RPM userland. |
-opensuse |
opensuse/leap:16.0 |
3.13 | SUSE / SLES family. |
-amazonlinux |
amazonlinux:2023 |
3.11 | AWS-centric deployments. |
-distroless |
gcr.io/distroless/python3-debian13 |
3.13 | No shell or package manager; minimal attack surface. |
# e.g. the Alpine variant, pinned to a version:
docker run --rm \
-v "$PWD/cronstable.yaml:/etc/cronstable.d/cronstable.yaml:ro" \
ghcr.io/ptweezy/cronstable:1.2.60-alpineBecause cronstable is a pure-Python app that supports any Python >= 3.10, behavior is identical across variants. Pick the base, not the interpreter version.
The Debian default covers the most architectures. Each variant covers the architectures its base image publishes:
- Alpine matches Debian's full set.
- Ubuntu covers
amd64,arm64,arm/v7,ppc64le,s390x, andriscv64. - RHEL, Fedora, and openSUSE cover
amd64,arm64,ppc64le, ands390x. - Amazon Linux and distroless cover
amd64andarm64.
All variants share the same non-root, read-only-friendly hardening as the default image.
The Rehydrate Docker images workflow
runs daily at 06:23 UTC and supports manual runs from main. GitHub Actions
can delay scheduled runs. Each run rebuilds the latest stable release's full
image matrix, including the amd64v3 variants, from its recorded commit.
It pulls fresh base images and rebuilds every Docker layer and optional
cryptography wheel. Dependency versions follow the release's constraints.
Updating exact pins, including the custom Python runtime, requires a new release.
Publication requires passing tests on Python 3.11 through 3.14 and runtime checks on every image platform. Each distro publishes once all of its own platforms pass, so a failed platform delays only its distro. The workflow verifies each image manifest. Trivy scans each refreshed image for high or critical vulnerabilities with an available fix, and a finding blocks publication only when the currently published image does not have it. Findings that the published image shares still need a fix from the base image or distribution, so the refresh publishes and lists them in the run summary. Scan reports for both images remain in the workflow artifacts for 30 days. A newer release or a moved release tag cancels publication of the older rebuild.
Successful refreshes update latest and the current release's version tags,
including their distro and CPU suffixes, on GHCR and the configured Docker Hub
repository. A version tag pins the application version; its dependencies can
change. Each run also publishes a tag such as
1.2.3-rebuild-20260917-123456-1-alpine, whose date, run ID, and attempt identify
the build. Pin that tag or an image digest to retain specific image bytes.
Historical release tags remain at their existing builds.
Refreshed images contain their matching zeroconf source archive and notices at
/usr/share/doc/cronstable/third-party/. Pull the updated image and recreate
your containers to apply a refresh. For a Compose deployment that uses published
images, run docker compose pull followed by docker compose up -d.
Python >= 3.10 is required. Install cronstable in a virtual environment:
python3 -m venv cronstableenv
. cronstableenv/bin/activate
pip install cronstableThis installs the cronstable console script (entry point
cronstable.__main__:main). For systems with an older Python, use the standalone
binary instead.
If you plan to use the Kubernetes leadership backend with the optional native
client library (cluster.kubernetes.clientLibrary: native), install the extra:
pip install "cronstable[kubernetes]". The default HTTP transport needs no extra
dependency. See
clustering and leader election.
On a 32-bit userland with a 64-bit kernel, install the push extra as
linux32 pip install "cronstable[push]". Under linux32, uname reports
the 32-bit machine, so pip skips cryptography, which has no 32-bit wheel,
rather than failing to build it from source.
On Python 3.15, the speedups extra skips isal, which has no wheel for
that version, so the daemon compresses responses with the standard library's
zlib. The orjson wheels for Python 3.15 on glibc Linux require glibc 2.39
or later, which Debian 13 and Ubuntu 24.04 provide. On an older glibc, pip
builds orjson from source, which requires a Rust toolchain.
pipx creates the virtualenv and installs the program into it:
pipx install cronstablepipx still requires a supported Python (3.10 or newer) available to build the isolated environment.
On macOS or Linux, install from the cronstable Homebrew tap:
brew install ptweezy/tap/cronstableThis installs the self-contained release binary for your platform (signed and
notarized on macOS; glibc amd64/arm64 from Homebrew on Linux), so no Python
is required. Upgrade later with brew upgrade cronstable.
macOS has neither cgroup v2 nor Job Objects, so
resourceLimits caps are not enforced there. On Linux,
the caps need a delegated cgroup.
On Windows, install the winget package:
winget install ptweezy.cronstableWinGet installs the package available in its catalog. The release workflow
submits signed setup executables for amd64 and arm64. Each setup embeds the
Windows MSI and displays cronstable's icon in the administrator
prompt. If the catalog lacks the package, download the setup executable or MSI
from the GitHub Release directly.
The installer needs administrator approval, adds the program to the system PATH,
and registers the Windows service. It includes Python. Open a new shell after
installation. The service starts at the next boot and runs no jobs until you
add configuration. To add a starter configuration and start the service now,
follow the MSI quick start.
To switch from a portable install to the service installer, back up any configuration stored beside the portable executable, then uninstall the portable package:
winget uninstall --exact --id ptweezy.cronstableInstall the setup package from the catalog:
winget install --exact --id ptweezy.cronstable --installer-type burn --scope machinePoint the service at your job configuration as described in
Windows MSI. You manage the configuration separately from
WinGet. To upgrade an MSI or setup installation, run
winget upgrade ptweezy.cronstable.
On Windows, through Scoop, using the manifest attached to each release:
scoop install https://github.com/ptweezy/cronstable/releases/latest/download/cronstable.jsonThis installs the same self-contained .exe the release publishes, for
whichever architecture you are on. Upgrade later with scoop update cronstable.
Releases attach a Debian package and an RPM for amd64, amd64v3, arm64, i686,
armv7, ppc64le, s390x and riscv64. Each installs the same self-contained
binary as /usr/bin/cronstable, plus a systemd unit and a starter
configuration:
| Path | Contents |
|---|---|
/usr/bin/cronstable |
The binary. |
/usr/lib/systemd/system/cronstable.service |
The service unit, running as the cronstable user. Delegate=memory pids hands cronstable the unit's cgroup with the memory and pids controllers, so each run of a job with resourceLimits can get a cgroup of its own with memory and process caps, and cronstable and its jobs share CPU with the other system services by nice value. CPU caps need Delegate=yes in a drop-in (see CPU caps and priority). OOMPolicy=continue, which systemd 243 and later already give a delegated unit, keeps the service running when a job is killed at its own memory cap. |
/etc/cronstable.d/cronstable.yaml |
Starter configuration, marked as a config file, so your edits survive an upgrade. |
/var/lib/cronstable |
The durable store, created by systemd on first start. |
The examples use amd64v3, recommended for compatible CPUs. Use amd64
in the filenames for the compatibility build, or choose your other
architecture. See CPU requirements.
# Debian, Ubuntu and derivatives
curl -fsSLO https://github.com/ptweezy/cronstable/releases/latest/download/cronstable-linux-amd64v3.deb
sudo apt install ./cronstable-linux-amd64v3.deb
# RHEL, Alma, Rocky, Fedora, SLES and derivatives
curl -fsSLO https://github.com/ptweezy/cronstable/releases/latest/download/cronstable-linux-amd64v3.rpm
sudo dnf install ./cronstable-linux-amd64v3.rpmInstalling creates the cronstable service account and leaves the service
stopped and disabled, because the shipped configuration schedules no jobs. Edit
/etc/cronstable.d/cronstable.yaml, then:
sudo systemctl enable --now cronstable
systemctl status cronstableThe packages declare the glibc version their binary needs, so a host too old for
a given architecture refuses the install rather than failing at first run.
Removing the package leaves /etc/cronstable.d and /var/lib/cronstable in
place; delete them by hand when you mean to drop the schedule and its history.
Resource caps need the unified cgroup v2 hierarchy. On a cgroup v1 host, the
caps are not enforced, and cronstable logs why when it loads the capped jobs.
systemd before 243 has no OOMPolicy= key: it logs a warning about the line
and ignores it. systemd before 236 cannot parse Delegate=memory pids and
leaves the unit undelegated; on such a host, set Delegate=yes in a drop-in.
On systemd before 240, systemctl reload cronstable fails once a
configuration with a capped job has loaded; see
the packaged systemd unit.
Both formats are vendor packages built from the release binary. They are not in Debian or Fedora proper and carry no distribution changelog or copyright file.
Releases attach an .apk for every architecture the musl builds cover. It
installs the binary, an OpenRC service and a starter configuration:
| Path | Contents |
|---|---|
/usr/bin/cronstable |
The binary, linked against musl. |
/etc/init.d/cronstable |
The OpenRC service, running as the cronstable user. |
/etc/conf.d/cronstable |
Service settings: config directory, user, state directory, stop timeout. |
/etc/cronstable.d/cronstable.yaml |
Starter configuration. |
/var/lib/cronstable |
The durable store, created by the service on first start. |
The example uses amd64v3, recommended for compatible CPUs. Use amd64
for the compatibility build if the CPU or VM lacks v3 or you are unsure.
See CPU requirements.
curl -fsSLO https://github.com/ptweezy/cronstable/releases/latest/download/cronstable-linux-amd64v3.apk
apk add --allow-untrusted ./cronstable-linux-amd64v3.apk--allow-untrusted is required: these packages are not signed with a key in
/etc/apk/keys, so apk declines them otherwise. Verify the download against the
release's SHA256SUMS if your policy calls for it.
Installing creates the cronstable service account and leaves the service
stopped, because the shipped configuration schedules no jobs and no Alpine
package enables its own service. Edit the configuration, then:
rc-update add cronstable default
rc-service cronstable start
rc-service cronstable statusrc-service cronstable reload reparses the configuration in place without
interrupting running jobs. The service runs under supervise-daemon, so a crash
is restarted, and stopping it gives cronstable two minutes to drain its running
jobs before they are killed (cronstable_retry in /etc/conf.d/cronstable).
The OpenRC service does not delegate a cgroup to the cronstable user, so
resourceLimits caps are not enforced under it, and the
daemon logs why.
Most Alpine users run the container image instead, which is built on the same musl base.
Releases attach a .pkg for amd64, amd64v3 and arm64, built by FreeBSD's own pkg in a
FreeBSD 14 virtual machine and installed there as part of the build:
| Path | Contents |
|---|---|
/usr/local/bin/cronstable |
The binary. |
/usr/local/etc/rc.d/cronstable |
The rc.d script, running the daemon under daemon(8) as the cronstable user. |
/usr/local/etc/cronstable.d/cronstable.yaml |
Starter configuration, installed from a .sample so your edits survive an upgrade. |
/var/db/cronstable |
The durable store, created by the rc.d script on first start. |
The example uses amd64v3, recommended for compatible CPUs. Use amd64
for the compatibility build, or arm64 for an ARM host.
See CPU requirements.
fetch https://github.com/ptweezy/cronstable/releases/latest/download/cronstable-freebsd-amd64v3.pkg
pkg add cronstable-freebsd-amd64v3.pkgpkg add of a local file needs no repository. Installing creates the
cronstable user and leaves the service disabled:
sysrc cronstable_enable=YES
service cronstable start
service cronstable statusservice cronstable reload sends SIGHUP to reparse the configuration in
place. Settings live in /etc/rc.conf: cronstable_config, cronstable_user,
cronstable_statedir and cronstable_flags.
FreeBSD has neither cgroup v2 nor Job Objects, so
resourceLimits caps are not enforced there, and each
configuration load logs a warning that names the capped jobs.
ubi installs release binaries straight
from GitHub, and mise drives it as a backend:
# ubi
ubi --project ptweezy/cronstable --in /usr/local/bin
# mise
mise use -g ubi:ptweezy/cronstableBoth pick the asset for your OS, architecture and libc from the release, so
there is nothing to configure. Pass --matching (or [matching=...] in mise)
to name a specific asset when you want one other than the default.
The repository is a flake, so it installs straight from the source tree:
nix run github:ptweezy/cronstable -- --version
nix profile install github:ptweezy/cronstableThis builds cronstable against nixpkgs' Python and dependency set rather than unpacking a release binary, so it needs no writable temp directory at startup. The flake covers Linux and macOS on x86_64 and aarch64.
A self-contained binary can be downloaded from https://github.com/ptweezy/cronstable/releases. Python is not required on the target system: the executable embeds it. Every release attaches the following assets, each built for its own platform and architecture. For x64, amd64v3 is recommended for compatible CPUs; amd64 is the compatibility build for CPUs or VMs without v3, or when you are unsure. See CPU requirements.
| Asset | Platform | libc / arch | Notes |
|---|---|---|---|
cronstable-linux-amd64v3 |
Linux | glibc, x86_64 / v3 | Recommended for compatible CPUs. Requires x86-64-v3; otherwise the same format and OS requirement as the amd64 row. |
cronstable-linux-amd64 |
Linux | glibc, x86_64 | Compatibility build. glibc 2.17 or newer: RHEL, Alma and Rocky 7 onward, Debian 8 onward, Ubuntu 14.04 onward, Amazon Linux 2 and 2023, SLES 12 onward. |
cronstable-linux-arm64 |
Linux | glibc, arm64 | glibc 2.17 or newer on arm64. |
cronstable-linux-i686 |
Linux | glibc, 32-bit x86 | 32-bit x86 (i686), glibc 2.36 or newer: Debian 12 i386 onward. |
cronstable-linux-armv7 |
Linux | glibc, 32-bit ARM | 32-bit ARM (armv7), glibc 2.31 or newer: Raspberry Pi OS bullseye, Debian 11, Ubuntu 20.04 onward. Raspberry Pi 2 and newer. |
cronstable-linux-armv6 |
Linux | glibc, 32-bit ARM | ARMv6 hard-float, glibc 2.36 or newer: the Raspberry Pi 1, Zero and Zero W running Raspberry Pi OS. |
cronstable-linux-armel |
Linux | glibc, 32-bit ARM | ARMv5 soft-float (Debian armel), glibc 2.36 or newer: Kirkwood devices such as the SheevaPlug, QNAP TS-x1x, D-Link DNS-320, Zyxel NSA325 and Pogoplug. |
cronstable-linux-ppc64le |
Linux | glibc, ppc64le | 64-bit little-endian POWER (IBM POWER), glibc 2.28 or newer: RHEL, Alma, and Rocky 8 onward, Debian 10 onward, Ubuntu 18.10 onward. |
cronstable-linux-s390x |
Linux | glibc, s390x | IBM Z (s390x, big-endian), glibc 2.17 or newer. |
cronstable-linux-riscv64 |
Linux | glibc, riscv64 | 64-bit RISC-V, glibc 2.41 or newer: Debian 13 onward. |
cronstable-linux-loong64 |
Linux | glibc, loongarch64 | LoongArch, glibc 2.41 or newer. New-world ABI only; the older Loongnix, Kylin and UOS fleets run a different, incompatible ABI. |
cronstable-linux-mips64le |
Linux | glibc, mips64el | 64-bit little-endian MIPS, glibc 2.36 or newer, such as Loongson and Cavium Octeon hardware. |
cronstable-linux-amd64v3-musl |
Linux | musl, x86_64 / v3 | Recommended for compatible CPUs. Requires x86-64-v3; otherwise the same format and OS requirement as the amd64 row. |
cronstable-linux-amd64-musl |
Linux | musl, x86_64 | Compatibility build. For Alpine and other musl hosts. |
cronstable-linux-arm64-musl |
Linux | musl, arm64 | For Alpine and other musl hosts. |
cronstable-linux-i686-musl |
Linux | musl, 32-bit x86 | 32-bit x86 (i686) for Alpine and other musl hosts. |
cronstable-linux-armv7-musl |
Linux | musl, 32-bit ARM | 32-bit ARM (armv7) for Alpine and other musl hosts. |
cronstable-linux-ppc64le-musl |
Linux | musl, ppc64le | 64-bit little-endian POWER for Alpine and other musl hosts. |
cronstable-linux-s390x-musl |
Linux | musl, s390x | IBM Z (s390x) for Alpine and other musl hosts. |
cronstable-linux-riscv64-musl |
Linux | musl, riscv64 | 64-bit RISC-V for Alpine and other musl hosts. |
cronstable-linux-armv6-musl |
Linux | musl, 32-bit ARM | ARMv6 hard-float for Alpine on that hardware. On Raspberry Pi OS use the glibc armv6 build instead. |
cronstable-linux-loong64-musl |
Linux | musl, loongarch64 | LoongArch for Alpine and other musl hosts. New-world ABI only. |
cronstable-linux-<arch>.deb |
Linux | glibc | Debian package for amd64, amd64v3, arm64, i686, armv7, ppc64le, s390x and riscv64. Installs the binary, a systemd unit and /etc/cronstable.d. |
cronstable-linux-<arch>.rpm |
Linux | glibc | RPM package for the same eight variants, with the same contents. |
cronstable-linux-<arch>.apk |
Linux | musl | Alpine package for amd64, amd64v3, arm64, i686, armv7, armv6, ppc64le, s390x, riscv64 and loong64. Installs the binary, an OpenRC service and /etc/cronstable.d. |
cronstable-macos-arm64 |
macOS | Apple Silicon (arm64) | Developer ID signed and notarized. |
cronstable-macos-amd64v3 |
macOS | Intel (x86_64) / v3 | Recommended for compatible CPUs. Requires x86-64-v3; otherwise the same format and OS requirement as the amd64 row. |
cronstable-macos-amd64 |
macOS | Intel (x86_64) | Compatibility build. Developer ID signed and notarized. |
cronstable-freebsd-amd64v3 |
FreeBSD | x86_64 / v3 | Recommended for compatible CPUs. Requires x86-64-v3; otherwise the same format and OS requirement as the amd64 row. |
cronstable-freebsd-amd64 |
FreeBSD | x86_64 | Compatibility build. For FreeBSD 14 and 15 hosts, including TrueNAS, pfSense and OPNsense. |
cronstable-freebsd-arm64 |
FreeBSD | arm64 | For FreeBSD 14 and 15 hosts on arm64. |
cronstable-freebsd-<arch>.pkg |
FreeBSD | amd64, amd64v3, arm64 | FreeBSD package. Installs the binary, an rc.d script and /usr/local/etc/cronstable.d. |
cronstable-openbsd-amd64v3 |
OpenBSD | x86_64 / v3 | Recommended for compatible CPUs. Requires x86-64-v3; otherwise the same format and OS requirement as the amd64 row. |
cronstable-openbsd-amd64 |
OpenBSD | x86_64 | Compatibility build. For OpenBSD 7.9. OpenBSD gives no cross-release ABI guarantee, so this asset tracks one release. |
cronstable-netbsd-amd64v3 |
NetBSD | x86_64 / v3 | Recommended for compatible CPUs. Requires x86-64-v3; otherwise the same format and OS requirement as the amd64 row. |
cronstable-netbsd-amd64 |
NetBSD | x86_64 | Compatibility build. For NetBSD 11.0. |
cronstable-illumos-amd64v3 |
illumos | x86_64 / v3 | Recommended for compatible CPUs. Requires x86-64-v3; otherwise the same format and OS requirement as the amd64 row. |
cronstable-illumos-amd64 |
illumos | x86_64 | Compatibility build. Built on OmniOS r151054 LTS; the illumos ABI is shared, so it also runs on OpenIndiana and in SmartOS zones. |
cronstable-windows-amd64v3.exe |
Windows | x64 (amd64) / v3 | Recommended for compatible CPUs. Requires x86-64-v3; otherwise the same format and OS requirement as the amd64 row. |
cronstable-windows-amd64.exe |
Windows | x64 (amd64) |
Compatibility build. Self-contained .exe. The target needs no Python. |
cronstable-windows-arm64.exe |
Windows | ARM64 | Self-contained .exe. The target needs no Python. |
cronstable-windows-i686.exe |
Windows | 32-bit x86 | Self-contained .exe for 32-bit Windows. |
cronstable-windows-amd64v3.zip |
Windows | x64 (amd64) / v3 | Recommended for compatible CPUs. Requires x86-64-v3; otherwise the same format and OS requirement as the amd64 row. |
cronstable-windows-amd64.zip |
Windows | x64 (amd64) |
Compatibility build. One-directory build: a cronstable\ folder with cronstable.exe and _internal\. Runs in place and can host the Windows service. |
cronstable-windows-arm64.zip |
Windows | ARM64 | One-directory build. Runs in place and can host the Windows service. |
cronstable-windows-i686.zip |
Windows | 32-bit x86 | One-directory build. Runs in place and can host the Windows service. |
cronstable-windows-amd64v3.msi |
Windows | x64 (amd64) / v3 | Recommended for compatible CPUs. Requires x86-64-v3; otherwise the same format and OS requirement as the amd64 row. |
cronstable-windows-amd64.msi |
Windows | x64 (amd64) | Compatibility build. Machine-wide installer. Registers the Windows service. See Windows MSI. |
cronstable-windows-arm64.msi |
Windows | ARM64 | Machine-wide installer. Registers the Windows service. See Windows MSI. |
cronstable-windows-i686.msi |
Windows | 32-bit x86 | Machine-wide installer. Registers the Windows service. See Windows MSI. |
cronstable-windows-amd64v3-setup.exe |
Windows | x64 (amd64) / v3 | Recommended for compatible CPUs. Requires x86-64-v3; otherwise the same format and OS requirement as the amd64 row. |
cronstable-windows-amd64-setup.exe |
Windows | x64 (amd64) | Compatibility build. Signed setup that embeds the MSI. WinGet installs this asset on x64. See Windows MSI. |
cronstable-windows-arm64-setup.exe |
Windows | ARM64 | Signed setup that embeds the MSI. WinGet installs this asset on ARM64. See Windows MSI. |
| x64 download | When to choose it |
|---|---|
amd64v3: recommended for compatible CPUs |
The CPU and guest OS expose the full x86-64-v3 feature set. |
amd64: compatibility build |
The CPU or VM lacks v3, or its capabilities are uncertain. |
amd64v3 means the x86-64-v3 microarchitecture level, not a different
operating-system ABI. It requires the complete v3 feature set, including AVX,
AVX2, BMI1/BMI2, F16C, FMA, LZCNT and MOVBE, with OS support for AVX register
state. Typical compatible CPUs include Intel Haswell and newer Core/Xeon
models and AMD Excavator/Zen, but model age or AVX2 alone is not a complete
compatibility check. Virtual machines must expose those features to the guest.
The upstream runtime documentation
explains the CPU levels. An incompatible CPU can terminate the binary with an
illegal-instruction error; choose baseline amd64 when unsure.
To select the recommended build on a compatible CPU, replace amd64 with
amd64v3 in the asset name, keeping its suffix. Examples are
cronstable-linux-amd64v3, cronstable-linux-amd64v3-musl,
cronstable-linux-amd64v3.deb, cronstable-freebsd-amd64v3.pkg and
cronstable-windows-amd64v3.msi. The .rpm, .apk, Windows .exe/.zip,
and macOS, OpenBSD, NetBSD and illumos binaries follow the same rule.
The macOS source build targets macOS 15 and newer.
The v3 Linux packages declare amd64/x86_64, the v3 FreeBSD package uses
the native FreeBSD ABI, and the v3 Windows MSI targets x64. Package managers
do not enforce the CPU requirement. These are alternative builds of the same cronstable package,
with the same install paths and services; choose one variant per installation.
Homebrew, Scoop and winget install the baseline downloads.
The optimized component is the embedded CPython runtime. Linux uses pinned python-build-standalone v3 builds; the other operating systems compile CPython with a v3 target (MSVC AVX2 on Windows). Compatible dependency wheels retain their upstream build settings. This is not a promise that every bundled library is rebuilt for v3 or that every workload becomes faster.
For compatible hosts, the recommended Docker tags are latest-amd64v3 (Debian),
latest-debian-amd64v3, and latest-<distro>-amd64v3 for the other distros.
Replace latest with the release version to pin one. They use the normal
linux/amd64 container platform and require a compatible host CPU. The bare
latest and latest-<distro> tags use the baseline amd64 build.
To build locally, add --platform linux/amd64 --build-arg PYTHON_VARIANT=amd64v3
to the selected Dockerfile's build command.
A binary embeds Python but not the C library, so the one number that decides whether it starts is the oldest glibc or musl it accepts. Each build declares that number, and CI re-derives it from the frozen bytes on every release, so the table above is measured rather than estimated.
amd64, amd64v3, arm64, and s390x need glibc 2.17, which reaches every glibc
distribution still in production, including the RHEL family from 7 onward
and Amazon Linux 2. They are built inside manylinux2014 containers against a
python-build-standalone
interpreter, which is where that number comes from. ppc64le needs 2.28: it
is built inside a manylinux_2_28 container so that it can carry the
cryptography wheel behind post-quantum push sealing, which exists for
POWER only at that floor. armv7 needs glibc 2.31; i686, armv6, armel,
and mips64le need 2.36; riscv64 and loong64 need 2.41. Each is set by
the oldest base image carrying a working toolchain for that architecture.
On 32-bit ARM the glibc version is only half the requirement, because there are
three incompatible ABIs. armv7 is hard-float ARMv7 and covers the Raspberry Pi
2 and newer plus Debian's own armhf port. armv6 is hard-float ARMv6, which is
what the Raspberry Pi 1, Zero and Zero W need: their ARM1176 core has neither
ARMv7 instructions nor a VFPv3 unit, so an armv7 binary faults on them. armel
is soft-float ARMv5 for Kirkwood hardware, which has no floating-point unit at
all. CI asserts all three from the frozen bytes, per bundled object, so a binary
cannot ship under the wrong one.
The musl builds need musl 1.2.5 or newer, which is Alpine 3.20 and later. They are built inside an Alpine container pinned to one Alpine release, so the requirement moves only when that pin does.
Raspberry Pi OS is glibc, so the binary for a Pi is always one of the glibc
builds: cronstable-linux-armv6 on a Pi 1, Zero or Zero W, armv7 on a 32-bit
install of anything newer, and arm64 on a 64-bit install, which is the default
on a Pi 3 and later. The -musl ARM builds are for Alpine on that hardware.
The -musl builds are not a fallback for an older glibc host. They are
dynamically linked against musl and name /lib/ld-musl-<arch>.so.1 as their
interpreter, so on a glibc machine the kernel finds no interpreter and the shell
prints not found, which reads like a corrupt download rather than the
wrong-libc error it is. When your glibc is older than a build needs, install
from PyPI, use the container image, or use the .deb or .rpm, which declines
the install outright instead of failing at first run.
The i686, armv7, ppc64le and s390x builds, both glibc and musl, extend
the 64-bit amd64/arm64 binaries to 32-bit x86, 32-bit ARM, POWER, and IBM Z
hosts. They build inside a container: amd64, arm64 and i686 natively on
their runners, the rest under QEMU emulation. The riscv64 builds cover 64-bit
RISC-V for both glibc and musl. The musl armv6 build extends to older
32-bit ARM, such as a Raspberry Pi 1 or Zero; its glibc twin comes from the
separate lane described later on this page, beside armel.
Each build bundles the optional extras its build lane can take a wheel for
or compile. cryptography, which carries post-quantum push sealing, takes a
wheel where PyPI has one and is built from source everywhere else, so every
build can seal the xwing suite except linux-mips64le and linux-armel,
which seal x25519 only. Intel macOS (macos-amd64 and macos-amd64v3)
and windows-i686 attempt patched cryptography source builds instead of
bundling the vulnerable 48.x release line. If compilation or the sealing
probe fails, the binary seals x25519 only, and a device paired under xwing
must pair again under x25519.
See Push notifications for what the source builds
promise and how to check a binary.
isal, the SIMD gzip library in the speedups extra, is bundled only from a
wheel. The amd64, amd64v3, and arm64 builds for Linux (both libcs),
macOS, and Windows include it. The other builds gzip web and cluster responses with
the standard library's zlib, which produces the same format more slowly.
The mips64le build covers 64-bit little-endian MIPS on glibc hosts. It is
built in an emulated Debian bookworm container, the last Debian suite that
carries this port, pinned to a snapshot of that archive because bookworm's LTS
phase publishes no further mips64el updates. Everything in it is compiled from
source, because no MIPS wheels are published. orjson is the one piece that
does not build there, so that binary uses the standard library JSON encoder.
There is no musl MIPS build, because Alpine has no MIPS port.
The loong64 builds cover LoongArch for both libcs, compiled from source under
emulation. They target the new-world ABI that upstream Debian and Alpine use.
Hardware running an old-world distribution (Loongnix, Kylin, UOS) is a different
ABI, and these binaries do not run there.
The glibc armv6 and armel builds compile from source too, on Raspbian
bookworm and Debian bookworm armel respectively, against the Python 3.11 those
suites package. Both run under an emulator pinned to the real target core, an
ARM1176 for armv6 and an ARM926 for armel, so an instruction the hardware
lacks faults during the build rather than on a user's machine. The armv6
build takes its cryptography from a wheel that a separate job compiles
ahead of it in the same image, because that one compile runs for
hours under emulation; when that job fails, the binary seals x25519 only.
macOS builds cover both Apple Silicon and Intel.
The FreeBSD builds cover amd64 and arm64 and run on FreeBSD 14 and 15
hosts, including appliance systems such as TrueNAS, pfSense and OPNsense.
They are built in a FreeBSD 14 virtual machine. PyPI publishes no FreeBSD
wheels, so the optional speedups are compiled from source; the arm64 build
ships without orjson and uses the standard library JSON encoder.
The OpenBSD, NetBSD and illumos builds are amd64, each built in a virtual
machine of that system, since no runner offers one and PyInstaller cannot
cross-compile. The OpenBSD binary is tied to OpenBSD 7.9: that system offers no
cross-release ABI guarantee, so a new asset follows each release. It ships
without orjson, because OpenBSD's Rust is older than that package's minimum,
and uses the standard library JSON encoder. The illumos binary is built on
OmniOS r151054 LTS and runs anywhere the illumos ABI does, including
OpenIndiana and SmartOS zones; nothing is prebuilt for illumos, so it ships
without orjson and without the push extra.
The Windows binaries are self-contained .exe files for x64 (amd64),
ARM64, and 32-bit x86 (i686). Like the other binaries they embed Python,
so Python is not required on the target. Use i686 only on a 32-bit
Windows install. On x64, choose amd64v3 for compatible CPUs or amd64
for compatibility; on ARM64, choose arm64.
Download and run (recommended amd64v3 build for a compatible Linux CPU,
glibc shown). Use amd64 for the compatibility build if v3 support is absent
or uncertain. Append -musl on Alpine, or use cronstable-macos-<arch> on a
Mac or cronstable-freebsd-<arch> on FreeBSD:
curl -fsSL -o cronstable \
https://github.com/ptweezy/cronstable/releases/latest/download/cronstable-linux-amd64v3
chmod +x cronstable
./cronstable --versionOn Windows, download cronstable-windows-amd64v3.exe for a compatible x64
CPU, cronstable-windows-amd64.exe for compatibility,
cronstable-windows-arm64.exe on ARM64, or cronstable-windows-i686.exe on
32-bit Windows. Run it directly; no chmod is needed:
.\cronstable-windows-amd64v3.exe --versionThe Windows binaries carry a version resource and are Authenticode-signed
with Azure Artifact Signing. While the signing identity's reputation accrues,
the first run of a browser-downloaded copy can still be blocked by SmartScreen.
Choose More info, then Run anyway. If your policy calls for it,
verify the download against the release's SHA256SUMS.
The WinGet release workflow submits signed setup executables that embed the MSI and its one-directory payload, using the compatibility build on x64. See the WinGet installation instructions for package availability and steps to switch from a portable install. For Windows deployment details, see running on Windows.
Windows releases also attach cronstable-windows-amd64v3.zip (recommended
for compatible CPUs), cronstable-windows-amd64.zip (compatibility build),
cronstable-windows-arm64.zip and cronstable-windows-i686.zip,
one-directory builds of the same program.
Each extracts to a single cronstable\ folder and is the download that can
host the Windows service, which the one-file .exe
cannot.
Clear the Mark of the Web from the zip before extracting, so the extracted
files do not each carry it. Writing into C:\Program Files needs an elevated
PowerShell, so use one:
Unblock-File .\cronstable-windows-amd64v3.zip
Expand-Archive .\cronstable-windows-amd64v3.zip -DestinationPath 'C:\Program Files'
& 'C:\Program Files\cronstable\cronstable.exe' --versionFor managed machine-wide deployment (GPO, Intune, SCCM) there is also an MSI per architecture. See Windows MSI.
The macOS binaries are Developer ID code-signed (hardened runtime)
and notarized by Apple, so Gatekeeper accepts them and they run without first
clearing the quarantine attribute. No xattr -d com.apple.quarantine step is
needed before first run.
The standalone binary is a self-extracting PyInstaller executable: on each start
it unpacks its embedded Python runtime into a temporary directory and loads
shared libraries from there. It therefore needs a temporary directory that is
both writable and executable. On an ordinary system the default /tmp
already satisfies this, so no extra setup is required.
This matters only when you run the binary under a read-only root filesystem
(for example, a hardened container). With the root filesystem read-only, /tmp
is read-only too, and the binary stops at startup: Could not create temporary directory, or Error loading shared library …: Operation not permitted. Give
it a small writable and executable temp mount and it runs:
# Note `exec`: Docker's --tmpfs defaults to `noexec`, but the binary must be
# able to execute the libraries it unpacks.
docker run --rm --read-only \
--tmpfs /tmp:rw,exec,nosuid,nodev,size=64m \
-v "$PWD/cronstable.yaml:/etc/cronstable.d/cronstable.yaml:ro" \
your-image-with-the-binary -c /etc/cronstable.dRemedies:
-
Docker: mount an
rw,exectmpfs at/tmp.--tmpfsdefaults tonoexec, which fails; passexecexplicitly, as shown earlier. -
Kubernetes: mount an
emptyDirat/tmp(writable and executable by default; usemedium: Memoryfor a tmpfs). -
Any host: point the binary at another writable, executable directory
with
TMPDIR=/path.
This requirement is unique to the standalone binary. The published container
image and the pip/pipx installs run cronstable as a normal Python package with
the interpreter on disk, so they never self-extract and need no writable temp
directory. See production and container deployment.
On Windows the self-extracting .exe uses the standard Windows temp directory
(%TEMP%), which is writable and executable by default. The preceding
read-only-rootfs and noexec caveats are Linux-container concerns only. The
Windows zip and MSI installs keep their files on disk beside cronstable.exe
and never unpack at startup, so this requirement does not apply to them at all.
Start cronstable by giving it a configuration file or directory with -c. It
always runs in the foreground:
cronstable -c /etc/cronstable.dThe -c default is platform-specific. On POSIX it is /etc/cronstable.d. On
Windows it is the machine-wide %ProgramData%\cronstable when that directory
holds configuration, and otherwise %APPDATA%\cronstable (for example,
C:\Users\<you>\AppData\Roaming\cronstable, falling back to the user profile
~ if APPDATA is unset). cronstable init writes a commented starter
configuration into the default location.
The default shell also differs: /bin/sh on POSIX, and on Windows an empty
default that runs a string command through %ComSpec% (cmd.exe). On
Windows, press Ctrl+C to stop cronstable gracefully. It finishes running jobs
first, as SIGTERM does on POSIX.
Per-job user/group switching and unix:// web listeners are not available
on Windows. For the full details, see running on Windows.
See the command-line reference for all flags, and the configuration reference for the configuration schema. For Windows-specific behavior, see running on Windows.