ippo

taiscale on droidian

taiscale on droidian

Droidian gives you real Debian on a phone, and Tailscale gives that phone a stable address on your private network wherever you are. I use the combo to SSH home, and to expose a couple of local services without touching my router.

This is the exact setup I run on a redmi note 9s running Droidian. It has two parts: first the normal Tailscale client, then a tiny Go app that uses tsnet to publish a service over Tailscale Funnel.

Part 1: Install Tailscale on Droidian

Droidian tracks Debian bookworm, so we can use Tailscale’s official bookworm repo. You need sudo access.

1
2
3
4
5
6
7
# Add Tailscale's GPG key
sudo curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.noarmor.gpg \
  | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null

# Add the repository
sudo curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.tailscale-keyring.list \
  | sudo tee /etc/apt/sources.list.d/tailscale.list

Install and bring it up:

1
2
3
4
sudo apt update
sudo apt install tailscale -y

sudo tailscale up

Check you are connected:

1
tailscale status

That is enough if you just want the phone on your tailnet. For a public HTTPS endpoint without port forwarding, keep going.

Part 2: Tailscale tsnet Funnel service

tsnet lets an app bring its own Tailscale node. No system daemon needed, and with Funnel you get a https://yourname.ts.net address that proxies to a local port. I use it for Vaultwarden, but the pattern works for any HTTP service.

You will need Go:

1
sudo apt install golang -y

1. Set your variables

Edit these before you run anything.

1
2
3
4
5
SERVICE_NAME="vaultwarden"
HOSTNAME="vaultwarden"
LOCAL_PORT=8080
USER=$(whoami)
SERVICE_DIR="/home/droidian/tsnet/vaultwarden"

2. Create the workspace

1
2
mkdir -p "$SERVICE_DIR/state"
cd "$SERVICE_DIR"

3. Add your auth key

Create a .env file with a reusable auth key from your Tailscale admin console.

1
2
3
4
5
if [ ! -f ".env" ]; then
  echo 'TS_AUTHKEY=tskey-auth-YOURKEYHERE' > .env
  echo ".env created, edit it with your key"
  exit 0
fi

Generate the key at https://login.tailscale.com/admin/settings/keys with “Reusable” and “Ephemeral” checked if you want the node to disappear when the service stops.

4. Write the Go proxy

This is a minimal reverse proxy that listens on tsnet’s Funnel and forwards to localhost.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
cat > main.go << EOF
package main

import (
    "log"
    "net/http"
    "net/http/httputil"
    "os"
    "tailscale.com/tsnet"
)

func main() {
    srv := &tsnet.Server{
        Hostname: "$HOSTNAME",
        AuthKey:  os.Getenv("TS_AUTHKEY"),
        Dir:      "./state",
    }
    defer srv.Close()

    ln, err := srv.ListenFunnel("tcp", ":443")
    if err != nil {
        log.Fatal(err)
    }

    proxy := &httputil.ReverseProxy{
        Director: func(r *http.Request) {
            r.URL.Host = "localhost:$LOCAL_PORT"
            r.URL.Scheme = "http"
        },
    }

    log.Println("Starting reverse proxy for $SERVICE_NAME...")
    log.Fatal(http.Serve(ln, proxy))
}
EOF

5. Initialize the module

1
2
3
go mod init tsnet/$SERVICE_NAME
go mod tidy
go get tailscale.com/tsnet

6. Build

1
go build -o app

7. Create a systemd service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sudo tee /etc/systemd/system/${SERVICE_NAME}-funnel.service > /dev/null <<EOF
[Unit]
Description=Tailscale Funnel Proxy for $HOSTNAME
After=network.target

[Service]
EnvironmentFile=$SERVICE_DIR/.env
WorkingDirectory=$SERVICE_DIR
ExecStart=$SERVICE_DIR/app
Restart=always
User=$USER
Group=$USER

[Install]
WantedBy=multi-user.target
EOF

8. Enable and start

1
2
3
4
sudo systemctl daemon-reload
sudo systemctl enable ${SERVICE_NAME}-funnel.service
sudo systemctl start ${SERVICE_NAME}-funnel.service
sudo systemctl status ${SERVICE_NAME}-funnel.service

9. Fix permissions if needed

1
sudo chown -R $USER:$USER "$SERVICE_DIR"

You should now see https://vaultwarden.your-tailnet.ts.net live in your Tailscale admin, proxying to port 8080 on the phone. Funnel handles TLS automatically.

Notes from running this on a phone

  • State: the ./state folder holds your node keys. Back it up if you want the same hostname to survive a reinstall.
  • Security: Funnel is public internet. Put auth in front of anything sensitive. Vaultwarden already has it, for a raw dashboard add Tailscale serve ACLs or basic auth in the proxy.
  • Multiple services: duplicate the folder, change HOSTNAME and LOCAL_PORT, build a second binary. Each gets its own tsnet node.

That is the whole setup. One apt repo for the client, one 30-line Go program for Funnel, and your Droidian device behaves like a tiny home server.

comments powered by Disqus