CLI tool
containrrr/shoutrrr avatar
containrrr/shoutrrr

Shoutrrr puts every notification target behind one URL scheme

Notification library for gophers and their furry friends.

1,700 stars105 forksGoMIT

At a glance

What is it?
Shoutrrr is a Go library and CLI that sends to chat and push services through service URLs, so a Slack token and a Discord channel become configuration strings rather than two integrations in your code. The latest tagged release is v0.8.0 from 2023-08-20, and the last push was on 2026-07-01.
Who is it for?
Adopt Shoutrrr when your Go project notifies humans and you want the target list to live in configuration as a URL instead of in code, and when adding a new channel should be a string change rather than a dependency. Do not adopt it for high volume or for anything needing per-recipient delivery guarantees, since the API is a fire-and-forget Send.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 114 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The URL is the whole configuration surface

Shoutrrr describes service URLs such as slack://token-a/token-b/token-c and discord://token@channel as the unit of configuration. A target is a string, credentials are embedded in that string, and the scheme selects the service implementation.

That single decision is what the library is for, and it is borrowed from caronc/apprise, which Shoutrrr says it was heavily inspired by. Apprise spreads the same idea across a large body of Python; Shoutrrr is the Go-shaped version.

The consequence for your codebase is that adding a notification channel stops being an integration. There is no second SDK to import, no second auth flow, no second set of structs. You add a string to a config file and the existing Send call reaches it.

Two entry points, and the difference matters

There is a one-liner that builds and fires in one go:

go
url := "slack://token-a/token-b/token-c"
err := shoutrrr.Send(url, "Hello world (or slack channel) !")

And there is the sender form, which builds once and reuses:

go
url := "slack://token-a/token-b/token-c"
sender, err := shoutrrr.CreateSender(url)
sender.Send("Hello world (or slack channel) !", map[string]string { /* ... */ })

CreateSender takes variadic URLs, so one sender can cover several services and fan out a single message to all of them:

go
urls := []string {
  "slack://token-a/token-b/token-c"
  "discord://token@channel"
}
sender, err := shoutrrr.CreateSender(urls...)
sender.Send("Hello world (or slack channel) !", map[string]string { /* ... */ })

That extra map[string]string on Send is the parameter dictionary passed to the service, and the README elides its contents with a comment. The per-service key names are the part you will have to look up in the documentation at containrrr.dev/shoutrrr, not in the README.

If you call CreateSender once at startup you pay the setup cost a single time. If you call shoutrrr.Send per notification you rebuild the sender every time, which is fine for a script and wasteful in a long-running service.

Three install routes, one of them a shell script

The package installs with the Go toolchain:

bash
go install github.com/containrrr/shoutrrr/shoutrrr@latest

There is a snap for systems that prefer one:

bash
sudo snap install shoutrrr

Or build from a clone, which is what the CLI quick start assumes when it tells you to run build.sh first:

bash
go build -o shoutrrr ./shoutrrr

Reading that instruction closely matters. The README says to run build.sh and then use the shoutrrr executable, without saying what build.sh does. In a repository whose top level also contains generate-release-notes.sh and generate-service-config-docs.sh, running an unreviewed script to get a working binary is not obviously necessary, since `go install` achieves the same result without it.

The container route is small on purpose. The Dockerfile copies CA certificates from an Alpine stage into a scratch image and nothing else, so the result is the single binary and a trust store:

dockerfile
FROM alpine:3.23.4 as alpine
RUN apk add --no-cache ca-certificates
FROM scratch
COPY --from=alpine /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY shoutrrr/shoutrrr /
ENTRYPOINT ["/shoutrrr"]

Note this stage expects a binary already built outside the image; it does not compile.

A CI action wraps the same URL

Shoutrrr publishes a GitHub Action, containrrr/shoutrrr-action@v1, which is the shortest path to a deploy notification. The workflow passes the URL as a secret and the message as ordinary expression interpolation:

yaml
name: Deploy
on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Some other steps needed for deploying
        run: ...
      - name: Shoutrrr
        uses: containrrr/shoutrrr-action@v1
        with:
          url: ${{ secrets.SHOUTRRR_URL }}
          title: Deployed ${{ github.sha }}
          message: See changes at ${{ github.event.compare }}.

The url input is a single string containing every target, so pointing CI at three channels means putting three service URLs in one secret. That is compact and it is also a single point of leakage: one secret value unlocks every destination, and rotating credentials means reissuing the whole string.

The repo also has a SECURITY.md, which is the place to report a vulnerability rather than opening a public issue.

What the CLI can and cannot do

The CLI surface is one verb with a simple shape:

shell
shoutrrr send [OPTIONS] <URL> <Message [...]>

Everything about it is positional and URL driven. There is no configuration file format documented in the README, no service listing command, and no retry or queue configuration. The library is the substance; the CLI exists so you can test a URL from a shell before wiring it into a service.

The dependency list shows what a notification client pulls in. cobra and viper handle the CLI and configuration, oauth2 covers services that authenticate through it, and fatih/color and mattn/go-colorable handle terminal output. The test stack is ginkgo with gomega and httpmock, which is a reasonable way to assert that a service request had the right shape without hitting a real endpoint.

The module declares go 1.25.0, so a build environment older than that will not resolve the module as written.

The release gap is the maintenance question

The tagged releases are v0.7.0 on 2023-01-25, v0.7.1 on 2023-01-29 and v0.8.0 on 2023-08-20. Nothing has been tagged since. The repository is not archived and the last push was on 2026-07-01, and the go.mod has been updated well past 2023, with dependencies such as cobra v1.10.2 and viper v1.21.0.

So the repository is receiving work while the release line has not moved in about three years. For a library whose integration surface is third party webhooks, that gap is not cosmetic: a service can change its API, and a fix that exists on main does not reach anyone who installed v0.8.0.

Two consequences. If you depend on Shoutrrr, you are depending on a set of services whose APIs the maintainers may no longer be testing against, and you should check each service you care about in the documentation before relying on it. And if you need a patch, installing from main is the only route, which trades release stability for currency.

The licence is MIT, which is permissive and imposes no obligation on the services you notify.

How it differs from apprise and from a vendor SDK

Apprise is the direct comparison and the source of the idea. Shoutrrr says it was heavily inspired by caronc/apprise. Shoutrrr is the Go implementation with a smaller service list, and it is the one that fits inside a Go binary you already ship.

A vendor SDK is the other comparison. Writing Slack and Discord separately means two modules, two auth implementations, two sets of error types, and two places where a token handling mistake can be made. Shoutrrr collapses that to one call and one credential format.

What it gives up is type safety at the boundary. With a vendor SDK, the compiler knows the webhook payload is a Slack block kit structure. With Shoutrrr, the destination is a string parsed at runtime, so a typo in a scheme is a runtime failure rather than a build failure. That is the trade: one integration surface in exchange for errors moving from compile time to run time.

Editorial conclusion

Adopt Shoutrrr when your Go project notifies humans and you want the target list to live in configuration as a URL instead of in code, and when adding a new channel should be a string change rather than a dependency. Do not adopt it for high volume or for anything needing per-recipient delivery guarantees, since the API is a fire-and-forget Send. Before pinning, note that v0.8.0 is the newest tag and dates from 2023-08-20, so check the services you depend on against the docs before assuming support, and decide early whether service URLs holding tokens belong in your secret store.

Frequently asked questions

How do I install Shoutrrr?

Three routes are documented: sudo snap install shoutrrr, go install github.com/containrrr/shoutrrr/shoutrrr@latest, or building from source with go build -o shoutrrr ./shoutrrr. The README's CLI section also tells you to run build.sh first, though go install avoids that script.

How do I send a notification to Slack and Discord from Go?

Build a sender with shoutrrr.CreateSender(urls...) passing the slack:// and discord:// URLs, then call sender.Send(message, params). CreateSender accepts several URLs, so one sender can fan a message out to more than one service.

How do I use Shoutrrr in a GitHub Actions workflow?

Use the containrrr/shoutrrr-action@v1 action and pass the service URL in the url input, normally from secrets, with title and message inputs for the content. The workflow example in the README does this on a push to main.

What is the latest release of Shoutrrr?

v0.8.0, released 2023-08-20, following v0.7.1 and v0.7.0 in January 2023. The repository is not archived and the last push was on 2026-07-01, so work has continued on main without a new tag.

Does Shoutrrr support Docker?

The repository contains a Dockerfile that copies CA certificates from an Alpine stage into a scratch image with the compiled binary and sets the entrypoint. It expects the shoutrrr binary to have been built beforehand rather than compiling inside the image.

Official sources

  1. containrrr/shoutrrr on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/containrrr-shoutrrr.svg)](https://hysenlabs.com/projects/containrrr-shoutrrr)

Was this useful?