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

Uploaded Python 3Windows x86-64

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

Uploaded Python 3

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

Uploaded Python 3

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

Uploaded Python 3macOS 11.0+ ARM64

caddytail-0.5.1-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.1.tar.gz.

File metadata

  • Download URL: caddytail-0.5.1.tar.gz
  • Upload date:
  • Size: 24.7 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.1.tar.gz
Algorithm Hash digest
SHA256 30726898b448e053c45986e1b737a27db961f0d716bbfbed9559bd3c47b92e48
MD5 e8911bcaaa3fbce63014e1cc1de59138
BLAKE2b-256 e75dc3b87896e18eaf6219fd1322f442d3ee50efb170dda467b783c810640117

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: caddytail-0.5.1-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.1-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 abb855cccbeb019aca04d7976c77d86a19d0a02cb683ff5e0d2cf78df03e8c3f
MD5 5ee0c5e5554dc8c4177ecc21b2285170
BLAKE2b-256 e70f356ce423d30f345ed2d940f1af55e150e007cc2b6e3547e704fab824b3ef

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for caddytail-0.5.1-py3-none-manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 dda4ba4b7d29e3338c6a0102e8e3c591003720b5802762676cac6864d105c9e5
MD5 9faa77ec1ebc448ec0bb999d9bc154ba
BLAKE2b-256 83d4b0e9ce6713e419ca8abd3f1690d17e233c2e5bac84df11a964fe972f6bd2

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for caddytail-0.5.1-py3-none-manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 ef02895d4541ab523348b6370700c65c5306529b38afab2f7a33f525bfc895d1
MD5 3010521aa8f7f0540dc8ed0099b2b29a
BLAKE2b-256 c81bd3c6115381af34fc9fce9de86ea877df53783b8e90b219322cbda6b7663d

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for caddytail-0.5.1-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 eb7d84104c616c70c77404e3ad5c5ecfd396acebb842fe435bc7f62abf889195
MD5 d4ddaae06f625dccc31bfc56c23001e4
BLAKE2b-256 fba6ce3bc1013a15cad17437db5e8091b31294d6f052a0128ee6efeddd36a482

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for caddytail-0.5.1-py3-none-macosx_10_15_x86_64.whl
Algorithm Hash digest
SHA256 32e9bec1a1338d4f7ab4898ad72ecf4bd5b8deae1390a9504bb1ac8d2ed7fe70
MD5 821d70e161c271e5565f74461e317cc9
BLAKE2b-256 3a437dbacbf8ee1576b1fc7db2859aa185f3c813acbae5d8e6e3709ab7d6a894

See more details on using hashes here.

Provenance

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