Manage Cloudflare Rules as IaC
Project description
octorules
Cloudflare Rules as code - Manage rules across zones declaratively
In the vein of infrastructure as code, octorules provides tools & patterns to manage Cloudflare Rules (Redirect Rules, Cache Rules, Origin Rules, WAF Custom Rules, Rate Limiting, and more) as YAML files. The resulting config can live in a repository and be deployed just like the rest of your code, maintaining a clear history and using your existing review & workflow.
octodns manages DNS records, but can't touch Cloudflare's newer Rules products. octorules fills that gap — one YAML file per domain, plan-before-apply, fail-fast on errors.
Getting started
Installation
pip install octorules
Configuration
Create a config file pointing at your zones:
# config.yaml
providers:
cloudflare:
token: env/CLOUDFLARE_API_TOKEN
rules:
directory: ./rules
zones:
example.com:
sources:
- rules
The env/ prefix resolves values from environment variables at runtime — keep secrets out of YAML.
Defining rules
Create a rules file for each zone:
# rules/example.com.yaml
redirect_rules:
- ref: blog-redirect
description: "Redirect /blog to blog subdomain"
expression: 'starts_with(http.request.uri.path, "/blog/")'
action_parameters:
from_value:
target_url:
expression: 'concat("https://blog.example.com", http.request.uri.path)'
status_code: 301
cache_rules:
- ref: cache-static-assets
description: "Cache static assets for 24h"
expression: 'http.request.uri.path.extension in {"jpg" "png" "css" "js"}'
action_parameters:
cache: true
edge_ttl:
mode: override_origin
default: 86400
Each rule requires a ref (stable identifier, unique within a phase) and an expression (Cloudflare ruleset expression). Optional fields include description, enabled (defaults to true), action, and action_parameters.
Usage
# Preview changes (dry-run)
octorules plan --config config.yaml
# Apply changes
octorules sync --doit --config config.yaml
# Validate offline (no API calls, useful in CI)
octorules validate --config config.yaml
# Export existing rules to YAML
octorules dump --config config.yaml
Supported phases
| YAML Key | Cloudflare Phase | Default Action |
|---|---|---|
redirect_rules |
http_request_dynamic_redirect |
redirect |
url_rewrite_rules |
http_request_transform |
rewrite |
request_header_rules |
http_request_late_transform |
rewrite |
response_header_rules |
http_response_headers_transform |
rewrite |
config_rules |
http_config_settings |
set_config |
origin_rules |
http_request_origin |
route |
cache_rules |
http_request_cache_settings |
set_cache_settings |
compression_rules |
http_response_compression |
compress_response |
custom_error_rules |
http_custom_errors |
serve_error |
waf_custom_rules |
http_request_firewall_custom |
(must specify) |
rate_limiting_rules |
http_ratelimit |
(must specify) |
Phases with a default action don't need action in the YAML — it's injected automatically. For waf_custom_rules and rate_limiting_rules, you must specify action explicitly (e.g., block, challenge, log).
CLI reference
octorules plan
Dry-run: shows what would change without touching Cloudflare. Exit code 2 when changes are detected. Output format and destination are controlled via manager.plan_outputs in the config file (defaults to text on stdout).
octorules plan [--zone example.com] [--phase redirect_rules] [--checksum]
octorules sync --doit
Applies changes to Cloudflare. Requires --doit as a safety flag. Atomic PUT per phase, fail-fast on errors.
octorules sync --doit [--zone example.com] [--phase redirect_rules] [--checksum HASH] [--force]
octorules compare
Compare local rules against live Cloudflare state. Exit code 1 when differences exist.
octorules compare [--zone example.com] [--checksum]
octorules report
Drift report showing deployed vs YAML source of truth.
octorules report [--zone example.com] [--output-format csv|json]
octorules validate
Validates config and rules files offline (no API calls). Useful in CI to catch errors early.
octorules validate [--zone example.com] [--phase redirect_rules]
octorules dump
Exports existing Cloudflare rules to YAML files. Useful for bootstrapping or importing an existing setup.
octorules dump [--zone example.com] [--output-dir ./rules]
Common flags
| Flag | Description |
|---|---|
--config PATH |
Path to config file (default: config.yaml) |
--zone NAME |
Process a single zone (default: all) |
--phase NAME |
Limit to specific phase(s); can be repeated |
--debug |
Enable debug logging |
--quiet |
Only show errors |
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success / no changes |
| 1 | Error |
| 2 | Changes detected (plan) |
Config reference
providers:
cloudflare:
token: env/CLOUDFLARE_API_TOKEN # env/ prefix reads from environment
max_retries: 2 # API retry count (default: 2)
timeout: 30 # API timeout in seconds (optional)
safety:
delete_threshold: 30.0 # Max % of rules that can be deleted (default: 30)
update_threshold: 30.0 # Max % of rules that can be updated (default: 30)
min_existing: 3 # Min rules before thresholds apply (default: 3)
rules:
directory: ./rules # Path to rules directory
manager:
max_workers: 4 # Parallel zone planning (default: 1)
plan_outputs: # Config-driven plan output (replaces --format/--output)
text:
class: octorules.plan_output.PlanText
html:
class: octorules.plan_output.PlanHtml
path: /tmp/plan.html # Optional: write to file instead of stdout
zones:
example.com:
sources:
- rules
allow_unmanaged: false # Keep rules not in YAML (default: false)
always_dry_run: true # Never apply changes (default: false)
safety: # Per-zone overrides
delete_threshold: 50.0
How it works
- Plan — Reads your YAML rules, fetches current rules from Cloudflare, computes a diff by matching rules on
ref. - Sync — Executes the plan by doing an atomic PUT per phase (full replacement of the phase ruleset). Fail-fast on errors.
- Dump — Fetches all rules from Cloudflare and writes them to YAML files, stripping API-only fields (
id,version,last_updated, etc.).
Safety features:
--doitflag — sync requires explicit confirmation.- Delete thresholds — blocks mass deletions above a configurable percentage.
- Checksum verification —
plan --checksumproduces a hash;sync --checksum HASHverifies the plan hasn't changed. - Auth error propagation — authentication and permission errors fail immediately instead of being silently swallowed.
- Failed phase filtering — phases that can't be fetched are excluded from planning to prevent accidental mass deletions.
CI/CD integration
# .github/workflows/rules.yaml
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install octorules
- run: octorules validate
plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install octorules
- run: octorules plan --checksum
deploy:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install octorules
- run: octorules sync --doit
Development
Local setup
git clone git@github.com:doctena-org/octorules.git
cd octorules
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
Running tests and linting
pytest
ruff check src/ tests/
ruff format --check src/ tests/
Releasing a new version
- Update the version in
pyproject.toml. - Commit and push to
main. - Tag the release and push the tag:
git tag v0.3.0
git push origin v0.3.0
Pushing a v* tag triggers the publish workflow, which builds the package, publishes it to PyPI, and creates a GitHub Release.
License
octorules is licensed under the Apache License 2.0.
Project details
Release history Release notifications | RSS feed
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 octorules-0.5.0.tar.gz.
File metadata
- Download URL: octorules-0.5.0.tar.gz
- Upload date:
- Size: 64.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bf7a55dcd0a041a7a3fa914600a08c771b7cf67c09977b0803f7db59c15f1226
|
|
| MD5 |
5be2f6cbffbc0765645820525f803ffe
|
|
| BLAKE2b-256 |
e4e004987abdbf58ce14970014aea227dfe190f72c825f5f7c956223b178c1dc
|
Provenance
The following attestation bundles were made for octorules-0.5.0.tar.gz:
Publisher:
publish.yaml on doctena-org/octorules
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
octorules-0.5.0.tar.gz -
Subject digest:
bf7a55dcd0a041a7a3fa914600a08c771b7cf67c09977b0803f7db59c15f1226 - Sigstore transparency entry: 957469801
- Sigstore integration time:
-
Permalink:
doctena-org/octorules@4df256180897cbcf117e6a0339ed38877732ba88 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/doctena-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@4df256180897cbcf117e6a0339ed38877732ba88 -
Trigger Event:
push
-
Statement type:
File details
Details for the file octorules-0.5.0-py3-none-any.whl.
File metadata
- Download URL: octorules-0.5.0-py3-none-any.whl
- Upload date:
- Size: 32.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
582c17a9299c9bb49a2fc9b0153187a399bde76547834db38583ccf9e7e40399
|
|
| MD5 |
2e9ad9dbe0964fd2f4aa14326a57bfb6
|
|
| BLAKE2b-256 |
348ab7501a03edff1541a9fc3bc5b7bde015471956c32985b2cdaa7361f3ebb8
|
Provenance
The following attestation bundles were made for octorules-0.5.0-py3-none-any.whl:
Publisher:
publish.yaml on doctena-org/octorules
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
octorules-0.5.0-py3-none-any.whl -
Subject digest:
582c17a9299c9bb49a2fc9b0153187a399bde76547834db38583ccf9e7e40399 - Sigstore transparency entry: 957469809
- Sigstore integration time:
-
Permalink:
doctena-org/octorules@4df256180897cbcf117e6a0339ed38877732ba88 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/doctena-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@4df256180897cbcf117e6a0339ed38877732ba88 -
Trigger Event:
push
-
Statement type: