Blog Logo

01-Jul-2025 ~ 4 min read

Exposing a Dockerized Python App on a Raspberry Pi Using Tailscale Funnel


๐Ÿš€ Exposing a Dockerized Python App on a Raspberry Pi Using Tailscale Funnel

As a developer hosting lightweight services on a Raspberry Pi, exposing them securely to the public internet can be tricky. Tools like ngrok or port forwarding either expire quickly, require fiddly router setup, or open up security holes.

Thatโ€™s where Tailscale comes in. Recently, I stumbled across it while looking for a simple, secure way to expose a Python app running in Docker on my Raspberry Pi and I was seriously impressed.

In this guide, Iโ€™ll walk you through how I used Tailscale Funnel to host a Dockerized Python app at https://mypiapp.example.com.

Table of contents

๐Ÿง  Why Tailscale?

Tailscale creates a peer-to-peer mesh VPN using WireGuard under the hood. But it doesnโ€™t stop at private networking. With the release of Tailscale Funnel, you can securely expose services to the public internet without any port forwarding or dynamic DNS.

Benefits:

  • ๐Ÿ”’ End-to-end encryption

  • ๐ŸŒ No port forwarding needed

  • ๐Ÿ†“ Free tier supports Funnel

  • ๐Ÿ› ๏ธ Custom domain support

๐Ÿ—๏ธ Our Setup

Weโ€™ll expose a Flask-based Python app running inside a Docker container on a Raspberry Pi.

Requirements:

  1. Raspberry Pi (or any Linux device) with Docker installed

  2. Tailscale account (Free works fine)

  3. A custom domain (e.g. example.com)

  4. Access to DNS settings (e.g., Cloudflare, Namecheap)

  5. Python Flask app (or similar)

๐Ÿณ Step 1: Dockerize the Python App

Letโ€™s start with a simple Flask app.

app.py

from flask import Flask
app = Flask(__name__)

@app.route("/")
def home():
    return "Hello from Raspberry Pi!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Dockerfile

FROM python:3.11-slim
WORKDIR /app
COPY app.py .
RUN pip install flask
CMD ["python", "app.py"]

docker-compose.yml (Optional)

version: '3'
services:
  piapp:
    build: .
    ports:
      - '5000:5000'
    restart: always

Then build and run:

docker compose up -d --build

Visit http://:5000 to verify itโ€™s working.

To verify Tailscale is running, you can check the status:

tailscale status --json

To get your Tailscale IP address, run:

tailscale ip -4

๐Ÿ” Step 2: Install & Authenticate Tailscale

Install Tailscale on the Pi:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

Open the URL printed in the terminal and authenticate your device.

๐ŸŒ Step 3: Enable Tailscale Funnel

Enable Funnel (public internet access) for this node:

sudo tailscale funnel enable

Then route public traffic to port 5000:

sudo tailscale serve tcp 443 http://localhost:5000

Boom! Your app is now live on:

https://<your-tailscale-username>.ts.net/

Test it from your phone or another device. It should show your Flask app.

๐ŸŒ Step 4: Connect a Custom Domain (mypiapp.example.com)

Now the fun part using your own domain.

  1. Add a CNAME DNS Record Go to your domain registrar and add a CNAME:
Type:    CNAME
Host:    mypiapp
Value:   <your-tailscale-username>.ts.net
TTL:     Auto or 5 min

If your DNS provider doesnโ€™t support CNAME at the root domain, you can use a subdomain like mypiapp.example.com.

  1. Configure HTTPS Tailscale will automatically provision a Letโ€™s Encrypt certificate for your .ts.net domain, but your custom domain still points there via CNAME so youโ€™re covered.

To verify:

curl -v https://mypiapp.example.com

๐Ÿ”„ Optional: Make Tailscale Serve Permanent

By default, tailscale serve is ephemeral. To persist it:

Create a tailscale serve config file using [tailscale up --advertise-exit-node + ACLs], or

Use [tailscale serve --bg] with systemd or a startup script.

A simple systemd service might look like:

[Unit]
Description=Tailscale Funnel for Pi App
After=network.target

[Service]
ExecStart=/usr/bin/tailscale serve tcp 443 http://localhost:5000
Restart=always

[Install]
WantedBy=multi-user.target

Save this as /etc/systemd/system/tailscale-funnel.service, then:

sudo systemctl enable --now tailscale-funnel

๐ŸŽ‰ Done! Youโ€™re Live. Youโ€™ve now got:

โœ… A Dockerized Python app โœ… Hosted on your Raspberry Pi โœ… Exposed via HTTPS using Tailscale โœ… Accessible from https://mypiapp.example.com without opening any ports

๐Ÿง  Final Thoughts

Tailscale Funnel is an absolute game-changer for developers running services on edge devices like Raspberry Pi. Itโ€™s dead simple, secure, and domain-friendly all without poking holes in your firewall.

If youโ€™re tired of fragile port forwarding, try this setup. It just works.

๐Ÿ”— Resources

Tailscale Funnel Docs

Tailscale Custom Domains Guide

Flask Docs

Docker Compose