Perfect Claude Code Notifications Setup with Tailscale and…
    Neura MarketNeura Market/DeepSeek
    ChatGPTChatGPTClaudeClaudeGeminiGeminiCursorCursorGrokGrokPerplexityPerplexityDeepSeekDeepSeek
    CoPilotCoPilotStable DiffusionStable DiffusionMidjourneyMidjourney
    View All Directories
    OverviewRulesPromptsMCPsAgentsGamesBlogVideosGuidesCoursesCommunityTrending
    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, running several sessions in parallel like Boris, 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 and forget that Claude is waiting for your input.

    Idea

    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 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 as the notification provider.

    To make sure that 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 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 for that. If you want to go with bash, it helps to have jq 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

    Go to the ACL editor and add a tag:container tag:

    "tagOwners": {
      "tag:container": ["autogroup:admin"]
    }
    

    Step 2: Create an OAuth Credential

    Go to 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 and binwiederhier/ntfy images and relies on Tailscale sidecar pattern 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 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 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. 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 and the App Store. 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. It handles multiple notification channels, sending to ntfy but also to native system notifications (in Mac, via 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

    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
    Five Gemma-4 models, one accelerator: what porting E2B 31B to AWS Inferentia2 taught megemma

    Five Gemma-4 models, one accelerator: what porting E2B 31B to AWS Inferentia2 taught me

    I ported the whole Gemma-4 family — E2B, E4B, 12B, 31B, and the 26B-A4B MoE — to run on...

    X
    xbill
    Hey DEV, I'm Tobore. Let's actually connect.community

    Hey DEV, I'm Tobore. Let's actually connect.

    Hey DEV, I'm Tobore. Let's actually connect. I've been on here for a while now, mostly writing and...

    L
    Laurina Ayarah
    I burned through thousands of AI tokens. Then a friend did it for freeai

    I burned through thousands of AI tokens. Then a friend did it for free

    (yep, kinda clickbait, just for the funsies 😊) At the beginning of the year, I relaunched my...

    P
    Paulo Henrique
    Claude might be saturating your machineai

    Claude might be saturating your machine

    My laptop was sitting idle with the fan at full tilt. Nothing was running that I knew of. The culprit...

    S
    Sidhant Panda
    Automated GitHub Code Reviews Using Google Geminigithubactions

    Automated GitHub Code Reviews Using Google Gemini

    I Built a Thing! TL;DR — Google Gemini-based Pull Request reviews and Issue Triaging for...

    D
    Darren "Dazbo" Lester
    What is an "agentic harness," actually?ai

    What is an "agentic harness," actually?

    I've been hearing the word "harness" thrown around a lot lately. I assumed it just meant "the IDE" or...

    T
    Tilde A. Thurium

    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.

    Neura Market

    Custom AI Systems & Services

    Our team of experienced AI builders will help build custom AI systems, workflows, and solutions for your business.

    Request custom work

    Ready-made automations for this

    Workflows from the Neura Market marketplace related to this DeepSeek resource

    • PostgreSQL Conversational Agent with Claude & DeepSeek (Multi-KPI, Secure)n8n · $14.99 · Related topic
    • Automate Blog Content Creation with Notion MCP, DeepSeek AI, and WordPressn8n · $9.99 · Related topic
    • Deploy a Customizable AI Chatbot with DeepSeek Integration on Your Websiten8n · $4.99 · Related topic
    • Automate Multi-Step Onboarding with Google Sheets, Forms, and Gmail Notificationsn8n · $24.99 · Related topic
    Browse all workflows