Perfect Claude Code Notifications Setup with Tailscale and ntfy — DeepSeek Blog | Neura Market
    Neura MarketNeura Market/DeepSeek
    ChatGPTChatGPTClaudeClaudeGeminiGeminiCursorCursorGrokGrokPerplexityPerplexityDeepSeekDeepSeek
    CoPilotCoPilotStable DiffusionStable DiffusionMidjourneyMidjourney
    View All Directories
    OverviewRulesPromptsMCPsAgentsBlogVideosGuidesCoursesCommunityTrendingGenerate
    DeepSeekBlogPerfect Claude Code Notifications Setup with Tailscale and ntfy
    Back to Blog
    Perfect Claude Code Notifications Setup with Tailscale and ntfy
    claudecode

    Perfect Claude Code Notifications Setup with Tailscale and ntfy

    Felipe Philipp February 26, 2026
    0 views

    If you’re like me and have been hooked into running Claude Code on your phone, running several...

    --- title: Perfect Claude Code Notifications Setup with Tailscale and ntfy published: true tags: [claudecode, tailscale, ntfy, docker] canonical_url: https://felipeelias.github.io/2026/02/25/claude-code-notifications.html --- If you’re like me and have been hooked [into running Claude Code on your phone](https://petesena.medium.com/how-to-run-claude-code-from-your-iphone-using-tailscale-termius-and-tmux-2e16d0e5f68b), running several [sessions in parallel like Boris](https://x.com/bcherny/status/2007179833990885678), you may have noticed that it is easy to lose track of what is going on on all those sessions. You may go away for a sec, distracted by [Minecraft parkour videos](https://www.youtube.com/watch?v=OqPxaKs8xrk) and forget that Claude is waiting for your input. ## Idea [Claude Code](https://code.claude.com/docs) comes with a [notification hook](https://code.claude.com/docs/en/hooks). Some terminals support it natively ([iTerm2](https://iterm2.com), [Kitty](https://sw.kovidgoyal.net/kitty/), [Ghostty](https://ghostty.org)) but most don’t, and even when they do, it’s a system notification which is easy to miss if you step away. The idea is to get a phone notification when Claude Code needs your input. I considered a few options, and I ended up choosing [ntfy](https://ntfy.sh) as the notification provider. To make sure that everything stays private, I decided to host ntfy on my machine and use [Tailscale](https://tailscale.com) as my private network. I was also tired of dealing with bash scripts. I kept running into compatibility issues between Mac, Linux and Windows, so I built a small tool to solve that (but you can still use bash). ## Requirements The only thing you need is a Tailscale account and [Docker](https://www.docker.com) for that. If you want to go with bash, it helps to have [`jq`](https://jqlang.org/) installed. ## Step 0: Project Structure Here are the files you’ll need: ``` my-infra/ ├── .env ├── compose.yml └── config/ └── ntfy.json ``` ## Step 1: Configure [Tailscale ACL](https://tailscale.com/kb/1068/acl-tags) Go to the [ACL editor](https://login.tailscale.com/admin/acls) and add a `tag:container` tag: ``` "tagOwners": { "tag:container": ["autogroup:admin"] } ``` ## Step 2: Create an [OAuth Credential](https://tailscale.com/kb/1215/oauth-clients) Go to [Trust & Credentials](https://login.tailscale.com/admin/settings/trust-credentials) to generate a new OAuth credential. 1. Click **Credential** → **OAuth** 2. Grant `auth_keys` scope with **write** permission 3. Select tag `tag:container` 4. Copy the client secret (`tskey-client-...`) OAuth works better because the regular auth keys expire in 1–90 days. OAuth client credentials don’t expire and the container re-authenticates automatically on restart. Now add the OAuth key to your `.env`: ``` TS_AUTHKEY=... ``` ## Step 3: Docker Compose Your compose will look like below. It uses the [`tailscale/tailscale`](https://hub.docker.com/r/tailscale/tailscale) and [`binwiederhier/ntfy`](https://hub.docker.com/r/binwiederhier/ntfy) images and relies on [Tailscale sidecar pattern](https://tailscale.com/docs/features/containers/docker) where it **exposes your Docker containers as machines** in the tailnet. This is really useful because you can reach the Docker container by name directly, the sidecar will proxy the request, handle HTTPS, etc. ``` name: my-infra services: ts-ntfy: image: tailscale/tailscale: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/tailscale - 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-state: ``` Note the [**`NTFY_UPSTREAM_BASE_URL`**](https://docs.ntfy.sh/config/#upstream-ntfysh) setting. This forwards push notifications through ntfy.sh’s Firebase/APNs infrastructure for instant mobile delivery. Without it, notifications can be delayed by minutes or hours. ## Step 4: [Tailscale Serve](https://tailscale.com/kb/1312/serve) Config `config/ntfy.json` — this tells Tailscale to proxy HTTPS to ntfy’s port 80: ``` { "TCP": { "443": { "HTTPS": true } }, "Web": { "${TS_CERT_DOMAIN}:443": { "Handlers": { "/": { "Proxy": "http://127.0.0.1:80" } } } } } ``` ## Step 5: Start It ``` docker compose up -d ``` Give it ~15 seconds for the TLS certificate to be provisioned. ntfy is now available at `https://ntfy.<your-tailnet>.ts.net` from any device on your tailnet. **Tip:** Your tailnet name (the `taila2944f` part) can be changed to something more readable in [DNS settings](https://login.tailscale.com/admin/dns). Also make sure that “HTTPS Certificates” are enabled. ## Step 6: Subscribe on Your Phone You need to install the ntfy app, available on [Google Play](https://play.google.com/store/apps/details?id=io.heckel.ntfy) and the [App Store](https://apps.apple.com/us/app/ntfy/id1625396347). Once installed you need to subscribe to a topic with your server URL. For example: 1. Add `claude-code` as the topic 2. Choose the custom server: `https://ntfy.<your-tailnet>.ts.net` You can make a quick test with: ``` curl -s -H "Title: Test" -d "Hello from the terminal!" "https://ntfy.<your-tailnet>.ts.net/claude-code" ``` ## Step 7: Claude Code Hook Now wire up Claude Code to send notifications through ntfy. You have a few options: ### Option 1: claude-notifier This is the tool I built to solve that: [claude-notifier](https://github.com/felipeelias/claude-notifier). It handles multiple notification channels, sending to ntfy but also to native system notifications (in Mac, via [`terminal-notifier`](https://github.com/julienXX/terminal-notifier)). {% embed https://github.com/felipeelias/claude-notifier %} Install it: ``` brew install felipeelias/tap/claude-notifier ``` Generate the config: ``` claude-notifier init ``` This creates `~/.config/claude-notifier/config.toml`. Point it to your ntfy server: ``` [[notifiers.ntfy]] url = "https://ntfy.<your-tailnet>.ts.net/claude-code" title = "Claude Code ({{.Project}})" ``` Then add the hook to `~/.claude/settings.json`: ``` { "hooks": { "Notification": [ { "hooks": [ { "type": "command", "command": "claude-notifier" } ] } ] } } ``` Same binary and config on every machine. Run `claude-notifier test` to verify it works. ### Option 2: Bash script You can still go with a bash script if you want. Create `~/.claude/hooks/notify.sh`: ``` #!/usr/bin/env bash set -euo pipefail # Convert backslashes for Windows path compatibility INPUT=$(cat | tr '\\' '/') PROJECT=$(printf '%s' "$INPUT" | jq -r '.cwd // empty' | xargs basename 2>/dev/null || echo "") HOOK_TITLE=$(printf '%s' "$INPUT" | jq -r '.title // empty') MESSAGE=$(printf '%s' "$INPUT" | jq -r '.message // "Done"') if [-n "$HOOK_TITLE"]; then TITLE="$HOOK_TITLE" elif [-n "$PROJECT"]; then TITLE="Claude Code ($PROJECT)" else TITLE="Claude Code" fi curl -s \ -H "Title: $TITLE" \ -d "$MESSAGE" \ "${NTFY_URL}/claude-code" > /dev/null 2>&1 || true ``` Make it executable (`chmod +x ~/.claude/hooks/notify.sh`) and add to `~/.claude/settings.json`: ``` { "env": { "NTFY_URL": "https://ntfy.<your-tailnet>.ts.net" }, "hooks": { "Notification": [ { "hooks": [ { "type": "command", "command": "~/.claude/hooks/notify.sh" } ] } ] } } ``` This requires `jq` (`brew install jq`, `apt install jq`, or `winget install jqlang.jq`). ## Putting all together If all is working you should see this: ![ntfy notification on phone](https://felipeelias.github.io/assets/images/ntfy.webp) ## Troubleshooting If the notification says “New message”, make sure that all devices (including your phone) are on the same Tailscale network. If they are and you’re still not getting notifications, you can always ask Claude to help you debug it.

    Tags

    claudecodetailscalentfydocker

    Comments

    More Blog

    View all
    How I'm using ASTs and Gemini to solve the "Codebase Onboarding" problem 🧠ai

    How I'm using ASTs and Gemini to solve the "Codebase Onboarding" problem 🧠

    Hi everyone! 👋 I’m Tara, a Senior Software Engineer and Consultant. Over the years, I've jumped...

    T
    tworrell
    Local AI Will Save Us All (The Math Says So, Trust Me)ai

    Local AI Will Save Us All (The Math Says So, Trust Me)

    Every few weeks a take goes viral in tech circles making the case for ditching cloud AI and running...

    S
    Sebastian Schürmann
    Lost in the AI Hype, I Started Smallai

    Lost in the AI Hype, I Started Small

    And it helped me get back into tech without drowning TL;DR at the end Coming back to...

    R
    Rohini Gaonkar
    Building a Replay-Tested Interactive Brokers Client in Gogo

    Building a Replay-Tested Interactive Brokers Client in Go

    I wanted an IBKR library that felt like Go and had testing I could trust. So I wrote one.

    T
    Thomas Marcelis
    Playwright in Pictures: Fully Parallel Modeplaywright

    Playwright in Pictures: Fully Parallel Mode

    Playwright’s fullyParallel mode is often treated as a simple performance switch. In practice, it...

    V
    Vitaliy Potapov
    Designing a CLI for Both Humans and Agentscli

    Designing a CLI for Both Humans and Agents

    Learn how Alpic designed its CLI for both human developers and AI agents — covering tradeoffs like polling, context windows, interactivity, and statelessness.

    J
    Julien Vallini

    Stay up to date

    Get the latest DeepSeek prompts, rules, and resources delivered to your inbox weekly.

    Neura Market LogoNeura Market

    Discover the best AI prompts, plugins, and resources for DeepSeek and more.

    Content Types

    • Rules
    • Prompts
    • MCPs
    • Agents
    • Guides

    Platforms

    • ChatGPT Directory
    • Claude Directory
    • Gemini Directory
    • Cursor Directory
    • Grok Directory
    • Perplexity Directory
    • DeepSeek Directory
    • CoPilot Directory
    • Stable Diffusion Directory
    • Midjourney Directory
    • All Directories

    Resources

    • Blog
    • Documentation
    • Help Center
    • Marketplace

    Legal

    • Privacy Policy
    • Terms of Service

    © 2026 Neura Market. All rights reserved.

    |

    Not affiliated with any AI platform vendors.