Skip to main content

Caddy web server with Tailscale plugin, packaged for pip installation

Project description

caddytail

Caddy web server with the Tailscale plugin, packaged for pip installation. Run any Python web app on your tailnet with one command — Flask, FastAPI, Django, or any WSGI/ASGI callable.

Installation

pip install caddytail              # bare WSGI apps — no extra deps
pip install caddytail[flask]       # Flask support
pip install caddytail[fastapi]     # FastAPI / Starlette support

Quick Start

Write a normal Flask app — no CaddyTail-specific setup needed:

# app.py
from flask import Flask
from caddytail import get_user

app = Flask(__name__)

@app.get("/")
def index():
    user = get_user()
    return f"Hello, {user.name}!"

Or use any WSGI callable — no framework required:

# app.py
from caddytail import get_user

def app(environ, start_response):
    user = get_user(environ)
    body = f"Hello, {user.name}!" if user else "Not authenticated"
    start_response("200 OK", [("Content-Type", "text/plain")])
    return [body.encode()]

Run it on your tailnet:

caddytail run myapp app:app

That's it. Your app is now available at https://myapp.<tailnet>.ts.net with Tailscale authentication.

CLI

Hostname is always the first positional argument:

# Development — foreground, Ctrl-C kills everything
caddytail run <hostname> <app_ref> [--debug]

# Production — install as systemd service + tail logs
caddytail install <hostname> <app_ref> [--no-start] [--env K=V]

# Service management
caddytail status <hostname>
caddytail logs <hostname> [-n LINES] [-f]
caddytail restart <hostname>
caddytail uninstall <hostname>

# List all installed services
caddytail list

# Raw Caddy pass-through
caddytail caddy [args...]

The <app_ref> format is module:variable (like uvicorn), defaulting the variable to app:

  • app:app — import app from app.py
  • myproject.main:application — import application from myproject/main.py
  • app — shorthand for app:app

Behavior

  • run — starts Caddy + your app in the foreground. Ctrl-C kills everything. The framework is auto-detected: Flask and FastAPI get framework-specific middleware; generic WSGI apps are served with wsgiref; generic ASGI apps are served with uvicorn.
  • install — writes a systemd unit file (ExecStart = caddytail run ...), enables, starts. If stdout is a tty, automatically tails logs. Ctrl-C stops tailing but leaves the service running.
  • uninstall — stops, disables, and removes the unit file.
  • caddy — passes all remaining args to the bundled Caddy binary.

Python API

get_user()

Returns a TailscaleUser with .name, .login, .profile_pic:

from caddytail import get_user

# Flask — no arguments needed (uses flask.request automatically)
user = get_user()

# FastAPI / Starlette — pass the Request object
user = get_user(request)

# WSGI — pass the environ dict
user = get_user(environ)

# Django — pass request.META
user = get_user(request.META)

if user:
    print(user.name)        # "John Doe"
    print(user.login)       # "john@example.com"
    print(user.profile_pic) # "https://..."

login_required

Works as both a Flask decorator and a FastAPI Depends() target:

from caddytail import login_required

# Flask
@app.get("/secret")
@login_required
def secret():
    user = get_user()
    return f"Hello, {user.name}!"

# FastAPI
@app.get("/secret")
async def secret(user=Depends(login_required)):
    return {"message": f"Hello, {user.name}!"}

static()

Register static file paths to be served directly by Caddy:

from caddytail import static

static(app, "/assets/*", "./static")
static(app, "/uploads/*", "/var/www/uploads")

The runner picks these up automatically when starting Caddy.

CaddyTail class

For programmatic use (most users should use the CLI runner instead):

from caddytail import CaddyTail

caddy = CaddyTail(app, "myapp", debug=True)
caddy.run()

All ports are auto-allocated. No conflicts when running multiple apps.

Framework Examples

FastAPI

from fastapi import FastAPI, Request, Depends
from caddytail import get_user, login_required

app = FastAPI()

@app.get("/")
async def index(request: Request):
    user = get_user(request)
    return {"message": f"Hello, {user.name}!"}

@app.get("/protected")
async def protected(user=Depends(login_required)):
    return {"message": f"Hello, {user.name}!"}

Django

# views.py
from django.http import HttpResponse
from caddytail import get_user

def index(request):
    user = get_user(request.META)
    return HttpResponse(f"Hello, {user.name}!")

Bare WSGI

from caddytail import get_user

def app(environ, start_response):
    user = get_user(environ)
    body = f"Hello, {user.name}!" if user else "Not authenticated"
    start_response("200 OK", [("Content-Type", "text/plain")])
    return [body.encode()]

ASGI

from caddytail import get_user

async def app(scope, receive, send):
    # For ASGI apps, extract headers from the scope manually
    ...

All examples are run the same way:

caddytail run myapp myproject:app

Supported Platforms

Pre-built wheels are available for:

Platform Architecture
Linux (glibc) x86_64, aarch64
macOS x86_64 (Intel), arm64 (Apple Silicon)
Windows x86_64

Building from Source

git clone https://github.com/jpc/caddytail
cd caddytail

# Install Go and xcaddy
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest

# Build caddy with the tailscale plugin
xcaddy build --with github.com/tailscale/caddy-tailscale --output src/caddytail/bin/caddy

# Build the wheel
pip install build
python -m build --wheel

License

This project packages Caddy (Apache 2.0 License) with the Tailscale plugin (BSD 3-Clause License).

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

caddytail-0.4.0.tar.gz (24.5 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

caddytail-0.4.0-py3-none-win_amd64.whl (21.4 MB view details)

Uploaded Python 3Windows x86-64

caddytail-0.4.0-py3-none-manylinux2014_x86_64.whl (21.2 MB view details)

Uploaded Python 3

caddytail-0.4.0-py3-none-manylinux2014_aarch64.whl (19.3 MB view details)

Uploaded Python 3

caddytail-0.4.0-py3-none-macosx_11_0_arm64.whl (19.9 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

caddytail-0.4.0-py3-none-macosx_10_15_x86_64.whl (21.4 MB view details)

Uploaded Python 3macOS 10.15+ x86-64

File details

Details for the file caddytail-0.4.0.tar.gz.

File metadata

  • Download URL: caddytail-0.4.0.tar.gz
  • Upload date:
  • Size: 24.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for caddytail-0.4.0.tar.gz
Algorithm Hash digest
SHA256 dab0ed05ba3a462b4f00fc078d803522c74c74aefee14548f753104da4595044
MD5 b2193895879d2bbc96daac0772611849
BLAKE2b-256 ada2bf5e40cc6323f0ecb819bb24c51293bb2d4cd85cdc7e47372954b84433b1

See more details on using hashes here.

Provenance

The following attestation bundles were made for caddytail-0.4.0.tar.gz:

Publisher: build.yml on jpc/caddytail

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file caddytail-0.4.0-py3-none-win_amd64.whl.

File metadata

  • Download URL: caddytail-0.4.0-py3-none-win_amd64.whl
  • Upload date:
  • Size: 21.4 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for caddytail-0.4.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 fcff5e72eaf242eac47e6f19cbc64529b727c060c32318fd61d2d18537632700
MD5 632e280bbd20c3010d62cca26fd1e2d5
BLAKE2b-256 72148ff34fd5b4f3b2ddeb1dbef461d44ba690e27a5f34c945780bb4d903ea1b

See more details on using hashes here.

Provenance

The following attestation bundles were made for caddytail-0.4.0-py3-none-win_amd64.whl:

Publisher: build.yml on jpc/caddytail

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file caddytail-0.4.0-py3-none-manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for caddytail-0.4.0-py3-none-manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 3e53df005b6065a4fb4337a415cb02bc920443560f277b424927b74b126c7dfb
MD5 7e0d69faff4a756e787b29175d878c0f
BLAKE2b-256 b35fdf3333d6fc35a36fcb2ef6c9661c458d01d0b23c1041cac7c4dc4569108c

See more details on using hashes here.

Provenance

The following attestation bundles were made for caddytail-0.4.0-py3-none-manylinux2014_x86_64.whl:

Publisher: build.yml on jpc/caddytail

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file caddytail-0.4.0-py3-none-manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for caddytail-0.4.0-py3-none-manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 984e72e8c1063fecbb4dde892c3d6b5f983b9bab0444684fe6cec63d95eab078
MD5 c7fd8ae9d79c51e73f8a25580ec3343a
BLAKE2b-256 c95d882be31d39d3ad0b5f9d8c69fe08860160313e0bed390bbccd221540deb4

See more details on using hashes here.

Provenance

The following attestation bundles were made for caddytail-0.4.0-py3-none-manylinux2014_aarch64.whl:

Publisher: build.yml on jpc/caddytail

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file caddytail-0.4.0-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for caddytail-0.4.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 f8a9f842c299dbab7655716f53e08ea96720912242f82a1d8e9706d9894b551e
MD5 db25a184b00269da3a626d963c361166
BLAKE2b-256 790bd8f4cc64eaa7d317b83ae439e3c7165c4971b3cf9f3e35dc8d8abc074e33

See more details on using hashes here.

Provenance

The following attestation bundles were made for caddytail-0.4.0-py3-none-macosx_11_0_arm64.whl:

Publisher: build.yml on jpc/caddytail

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file caddytail-0.4.0-py3-none-macosx_10_15_x86_64.whl.

File metadata

File hashes

Hashes for caddytail-0.4.0-py3-none-macosx_10_15_x86_64.whl
Algorithm Hash digest
SHA256 3d0a326549dab0e0e95edee5d1b558ad942da7f6508be71620c0843435cdc4c3
MD5 2ccaaf6b25d5c6879d63d902e30d7dcc
BLAKE2b-256 9a38a0f24acaf43eee676bf2e3c93ad9b253b663e058e46f211cf6e8747c229b

See more details on using hashes here.

Provenance

The following attestation bundles were made for caddytail-0.4.0-py3-none-macosx_10_15_x86_64.whl:

Publisher: build.yml on jpc/caddytail

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page