Beyond Slack

GitHub Actions notifications without Slack.

Your #deploys channel has 847 unread messages. Half are green checkmarks. The one red failure is buried. There is a better way.

The Slack CI notification problem

Every team starts the same way: "Let's send CI results to Slack!" Six months later:

The problem isn't Slack. It's that CI alerts and team chat are different things. One is a signal — something broke, act now. The other is a conversation. Mixing them degrades both.

The fix: failures-only push to your phone

name: Build & Test
on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build
        run: make build

      - name: Test
        run: make test

      - name: Alert on failure
        if: failure()
        env:
          NERVE_DSN: ${{ secrets.NERVE_DSN }}
        run: |
          go install github.com/nerve-ink/nerve-cli/cmd/nerve@latest
          echo "FAILED: ${{ github.repository }} (${{ github.ref_name }}) — ${{ github.event.head_commit.message }}" \
            | nerve send --severity critical

That's it. Green builds are silent. Red builds ping your phone. Encrypted.

Notify on deploy

deploy:
  runs-on: ubuntu-latest
  needs: build
  steps:
    - name: Deploy to production
      id: deploy
      run: ./deploy.sh production
      continue-on-error: true

    - name: Notify deploy result
      env:
        NERVE_DSN: ${{ secrets.NERVE_DSN }}
      run: |
        go install github.com/nerve-ink/nerve-cli/cmd/nerve@latest
        if [ "${{ steps.deploy.outcome }}" = "failure" ]; then
          echo "DEPLOY FAILED: ${{ github.repository }} → production" | nerve send --severity critical
        else
          echo "Deployed: ${{ github.repository }} → production (${{ github.sha }})" | nerve send
        fi

Multiple repos, one phone

Use the same NERVE_DSN across all repositories. Every failure from every repo arrives as a push on your phone. No more checking 12 Slack channels.

# Organization-level secret: Settings → Secrets → Actions
# Name: NERVE_DSN
# Value: nerve://TOKEN:[email protected]
# Access: All repositories

Reusable workflow

Create a reusable notification workflow to avoid repeating the step in every repo:

# .github/workflows/nerve-notify.yml (in a shared repo)
name: Nerve Notify
on:
  workflow_call:
    inputs:
      message:
        required: true
        type: string
      severity:
        required: false
        type: string
        default: "alert"
    secrets:
      NERVE_DSN:
        required: true

jobs:
  notify:
    runs-on: ubuntu-latest
    steps:
      - name: Send notification
        env:
          NERVE_DSN: ${{ secrets.NERVE_DSN }}
        run: |
          go install github.com/nerve-ink/nerve-cli/cmd/nerve@latest
          echo "${{ inputs.message }}" | nerve send --severity ${{ inputs.severity }}

Slack vs. Nerve for CI

Signal-to-noiseSlack channels mix CI, conversations, and bot spam. Nerve sends only what you tell it — typically failures only.
VisibilityA Slack message in a muted channel is invisible. A phone push notification gets seen.
Token securityA Slack bot token can read channels, messages, and files. A Nerve DSN can only send encrypted signals into one pipe.
Data privacySlack stores your CI output in plaintext. Nerve encrypts it before it leaves the runner.
CostSlack Pro is $8.75/user/month. Nerve is free during beta.

Setup in 2 minutes

  1. Go to your GitHub repo → Settings → Secrets → Actions
  2. Add NERVE_DSN with value nerve://TOKEN:[email protected]
  3. Add the failure notification step to your workflow
  4. Push a commit and break a test — your phone should buzz

FAQ

How do I get GitHub Actions notifications without Slack?

Add a nerve send step to your workflow with if: failure(). Store the DSN as a repo or org secret. You get a push on your phone, not a Slack message.

Why not just use Slack?

Slack channels get noisy and muted. Failures get buried. A leaked bot token reads your history. Nerve is failures-only, encrypted, and direct to your phone.

Can I notify only on failures?

Yes. Use if: failure() in the workflow step.