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.5.0.tar.gz (24.6 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.5.0-py3-none-win_amd64.whl (21.4 MB view details)

Uploaded Python 3Windows x86-64

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

Uploaded Python 3

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

Uploaded Python 3

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

Uploaded Python 3macOS 11.0+ ARM64

caddytail-0.5.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.5.0.tar.gz.

File metadata

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

File hashes

Hashes for caddytail-0.5.0.tar.gz
Algorithm Hash digest
SHA256 3648ad5710fa4031de6478d91800842b56820d05c28a2ff238623329daccdd9f
MD5 ea9756d3d8acbd38c43f699ce20c1962
BLAKE2b-256 a4d029cd28d4ac613941d3098360efcfdcb47f5391f252beb9ffa43255cfd6dd

See more details on using hashes here.

Provenance

The following attestation bundles were made for caddytail-0.5.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.5.0-py3-none-win_amd64.whl.

File metadata

  • Download URL: caddytail-0.5.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.5.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 438df94663c3c193e6e3992383b9c8e340b7c2148493d52fbed8e1bf9e476563
MD5 bc4b25cd77e35c6e3e303cdbe1d2de9f
BLAKE2b-256 acf4a1ca128acb023e41e07e495d3294bdebfd193a6df61da8971b26251686ee

See more details on using hashes here.

Provenance

The following attestation bundles were made for caddytail-0.5.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.5.0-py3-none-manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for caddytail-0.5.0-py3-none-manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 83513efdaf327e3d735f923d8e3208edf2ca2b43a65fa67ab819a98a274e98f6
MD5 c33c7aea5a009d62ba3d11ebcfb4ac76
BLAKE2b-256 a4f8f2c8e02df286c99f4e188b37684e81e39e4e01077db7991970a75baacddd

See more details on using hashes here.

Provenance

The following attestation bundles were made for caddytail-0.5.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.5.0-py3-none-manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for caddytail-0.5.0-py3-none-manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 063c60633d02b1c88061cb1135e3c06482c541a9c4dc60f1927b70ada95dfb62
MD5 44bed17063fe1ef8c0df6344488ab90d
BLAKE2b-256 3584de9fc2acb7dcbc86859c98e4c2852fff5f5b06244ca34007118aef7cca63

See more details on using hashes here.

Provenance

The following attestation bundles were made for caddytail-0.5.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.5.0-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for caddytail-0.5.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 fc06223b07253adc0ccceb2ac09f557ac01035ad8f0079672c0c5387f15354f2
MD5 0f71f564e82a8ca899bed01f670ff4a1
BLAKE2b-256 d5bf63fd9ef2913311f1734245740ae73dfbbe235a4dc5d5d9aca9e98f0570bc

See more details on using hashes here.

Provenance

The following attestation bundles were made for caddytail-0.5.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.5.0-py3-none-macosx_10_15_x86_64.whl.

File metadata

File hashes

Hashes for caddytail-0.5.0-py3-none-macosx_10_15_x86_64.whl
Algorithm Hash digest
SHA256 d06002dff4c4ec7b424e26d8d030d7bbc5bc1466b2b5a9acf76da6998a608e6f
MD5 9a7ac7df56b667bf52646b6d0c50c83d
BLAKE2b-256 e4d3aadf9fb0098c2e58539b8805a18519abb73ead9adc278e3f198a218e41a4

See more details on using hashes here.

Provenance

The following attestation bundles were made for caddytail-0.5.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