AI & ML DevOps General Tech Community Best Practices & Tools All News About Contact
advertisement
AI & ML

Perfect Claude Code Notifications Setup with Tailscale and ntfy

May 2026 8 min read
Back to AI & ML

If you're like me and have been hooked on running Claude Code on your phone, running multiple sessions in parallel like Boris, you'll have noticed that it's easy to lose track of what's going on in all those sessions. You can walk away for a second, get distracted by Minecraft parkour videos, and forget that Claude is waiting for your opinion.

ideas

Claude Code comes with a notification hook. Some terminals support it natively (iTerm2, Kitty, Ghostty), but most don't, and even when they do, it's a system notification that's easy to miss if you walk away.

The idea is to receive a phone notification when Claude Code needs your opinion. I considered a few options and ended up choosing ntfy as the notification provider.

To make sure everything stays private, I decided to host ntfy on my machine and use Tailscale as my private network.

I was also tired of dealing with bash scripts. I kept having compatibility issues between Mac, Linux and Windows, so I built a little tool to solve them (but you can still use bash).

Requirements

All you need is a Tailscale and Docker account for that. If you want to use bash, it helps to have jq

installed.

Step 0: Project Structure

These are the files you will need:

mi-infra/

├── .env

├── compose.yml

└── configuration/

└── ntfy.json

Step 1: Configure Backscaling ACL

Go to the ACL editor and add a tag: container

tag:

"Tag owners": {

"tag:container": ["autogroup:admin"]

}

Step 2 – Create an OAuth credential

Go to Trust and Credentials to generate a new OAuth credential.

- Click Credential → OAuth

- Scholarship

authentication_keys

writable scope - Select Tag

tag:container

- Copy the client secret (

tskey-client-...

)

OAuth works better because typical authentication keys expire between 1 and 90 days. OAuth client credentials do not expire and the container is automatically re-authenticated upon reboot.

Now add the OAuth key to your .env

:

TS_AUTHKEY=...

Step 3: Writing Docker

Your wording will look like below. Use tailscale/tailscale

and binwiederhier/ntfy

images and is based on the Tailscale sidecar pattern where you expose your Docker containers as machines on the back network. This is really useful because you can access the Docker container directly by name, the sidecar will render the request, handle HTTPS, etc.

name: mi-infra

services:

ts-ntfy:

image: queue scale/queue scale:latest

container_name: ts-ntfy

hostname: ntfy

restart: unless stopped

environment:

- TS_AUTHKEY=${TS_AUTHKEY}?ephemeral=false

- TS_EXTRA_ARGS=--advertise-tags=tag:container --reset

- TS_SERVE_CONFIG=/config/ntfy.json

- TS_STATE_DIR=/var/lib/backscale

- TS_USERSPACE=false

volumes:

- ts-ntfy-state:/var/lib/tailscale

- ./config:/config

devices:

- /dev/net/tun:/dev/net/tun

cap_add:

-net_admin

ntfy:

image: binwiederhier/ntfy

container_name: ntfy

restart: unless stopped

command: serve

environment:

NTFY_BASE_URL: "https://ntfy.<your-tailnet>.ts.net"

NTFY_UPSTREAM_BASE_URL: "https://ntfy.sh"

network_mode: service:ts-ntfy

depends_on:

-ts-ntfy

volumes:

ts-ntfy-status:

Note NTFY_UPSTREAM_BASE_URL

configuration. This forwards push notifications through ntfy.sh's Firebase/APN infrastructure for instant mobile delivery. Without it, notifications can be delayed by minutes or hours.

Step 4: Tailscale Service Configuration

config/ntfy.json

- this tells Tailscale to send HTTPS to ntfy port 80:

{

"TCP": {

"443": {

"HTTPS": true

}

},

"Website": {

"${TS_CERT_DOMAIN}:443": {

"Handlers": {

"/": {

"Proxy": "http://127.0.0.1:80"

}

}

}

}

}

Step 5: Get started

docker compose -d

Wait ~15 seconds for the TLS certificate to be provisioned. ntfy is now available at https://ntfy.<your-tailnet>.ts.net

from any device in your tailnet.

Tip: the name of your tailnet (the taila2944f

part) can be changed to something more readable in DNS settings. Also make sure "HTTPS Certificates" are enabled.

Step 6: Subscr

Related Coverage

AI & ML

DumbQuestion.ai - Self-Awareness, Prompt Injection, Search Intent... and darkness

AI & ML

Gemini 2.5 Flash vs Claude 3.7 Sonnet: 4 Production Constraints That Made the Decision for Me

AI & ML

I Made Claude Code Think Before It Codes. Here's the Prompt.