GitHub Actions push notifications
PushPiper pings your phone when a GitHub Actions workflow fails or a deploy lands, free for 10,000 messages a month. It's one run: curl step with your keys in repository secrets, no third-party action needed.
Before you start
- Sign in at pushpiper.com/app (free, no credit card) and create an app called GitHub. Its API token is your
APP_TOKEN. - Copy your user key from the dashboard. That's your
USER_KEY. - In your repository, go to Settings > Secrets and variables > Actions and add two repository secrets:
PUSHPIPER_TOKENandPUSHPIPER_USER.
Get a push when a build fails
name: CI
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- run: npm ci
- run: npm test
- name: Ping PushPiper on failure
if: failure()
env:
PUSHPIPER_TOKEN: ${{ secrets.PUSHPIPER_TOKEN }}
PUSHPIPER_USER: ${{ secrets.PUSHPIPER_USER }}
run: |
curl -sS --fail-with-body https://api.pushpiper.com/1/messages.json \
--data-urlencode "token=$PUSHPIPER_TOKEN" \
--data-urlencode "user=$PUSHPIPER_USER" \
--data-urlencode "title=$GITHUB_REPOSITORY: $GITHUB_WORKFLOW failed" \
--data-urlencode "message=Branch $GITHUB_REF_NAME, commit ${GITHUB_SHA::7}" \
--data-urlencode "priority=1" \
--data-urlencode "url=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
--data-urlencode "url_title=Open the run"if: failure() runs the step only when an earlier step failed. The notification goes out at priority 1, so it gets through quiet hours, and its link opens the failed run.
Keys and run details reach curl as environment variables instead of being pasted into the script, so a branch name with quotes can't break the command, and --data-urlencode takes care of the encoding.
Report every deploy
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- run: ./deploy.sh
- name: Ping PushPiper
if: always()
env:
PUSHPIPER_TOKEN: ${{ secrets.PUSHPIPER_TOKEN }}
PUSHPIPER_USER: ${{ secrets.PUSHPIPER_USER }}
JOB_STATUS: ${{ job.status }}
run: |
priority=0
if [ "$JOB_STATUS" != "success" ]; then priority=1; fi
curl -sS --fail-with-body https://api.pushpiper.com/1/messages.json \
--data-urlencode "token=$PUSHPIPER_TOKEN" \
--data-urlencode "user=$PUSHPIPER_USER" \
--data-urlencode "title=$GITHUB_WORKFLOW: $JOB_STATUS" \
--data-urlencode "message=$GITHUB_REPOSITORY on $GITHUB_REF_NAME" \
--data-urlencode "priority=$priority" \
--data-urlencode "url=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
--data-urlencode "url_title=Open the run"if: always() runs the step whether the job passed or not, and job.status says which. Failures and cancellations get priority 1, successes a normal ping.
Handy values for your message
| Variable | Holds |
|---|---|
GITHUB_REPOSITORY | Owner and repository, like acme/web |
GITHUB_WORKFLOW | The workflow's name |
GITHUB_REF_NAME | The branch or tag that triggered the run |
GITHUB_SHA | The full commit SHA. ${GITHUB_SHA::7} shortens it |
GITHUB_ACTOR | Who started the run |
GITHUB_SERVER_URL, GITHUB_RUN_ID | With the repository, the link to the run |
Every runner sets these. The full list is in GitHub's variables reference.
Limits and caveats
- Marketplace Pushover actions won't work. The ones we checked are hardwired to
api.pushover.net. - `--fail-with-body` needs curl 7.76 or later. GitHub-hosted Ubuntu runners have it. It fails the step on a 400 or 429 and prints PushPiper's error.
- Windows runners default to PowerShell. Add
shell: bashto the step. - Forks get no secrets. Workflows triggered from a forked repository don't receive
PUSHPIPER_TOKEN, so the step fails for outside pull requests. - Count your pings. Every notification is one message. Failure-only alerts barely dent 10,000 a month; pinging on every push in a busy repo can.
Sources
Questions and answers
Can I use a Pushover action from the GitHub Marketplace?
Not the ones we checked. desiderati/github-action-pushover, umahmood/pushover-actions and Clivern/pushover-actions all post to api.pushover.net with no way to change the host. A curl step works on any runner that has curl.
How do I get notified only when something fails?
Add if: failure() to the notification step. It then runs only when an earlier step in the same job failed.
How do I keep the token out of the logs?
Store it as a repository secret and hand it to the step through env, like the examples do. GitHub masks secret values in logs.
Does it work on self-hosted runners?
Yes, as long as the runner has bash and curl 7.76 or later and can reach api.pushpiper.com.