nicholas-fedor/shoutrrr

A notification library for gophers and their furry friends.

174

stars

1,741

commits

Go

primary language

Sep 9, 2026

updated

shoutrrr.nickfedor.com/
cli
go
golang
library
notification
spf13-cobra

README

Shoutrrr

A notification library for gophers and their furry friends.
Heavily inspired by caronc/apprise.

OpenSSF Scorecard codecov Codacy Badge github code size in bytes Pulls from DockerHub go.dev reference Ask DeepWiki All Contributors

license

Table of Contents

Full Documentation

Visit the project's GitHub Page for full documentation.

Installation

From Source

go install github.com/nicholas-fedor/shoutrrr/shoutrrr@latest

Binaries

Install the latest release binary to $HOME/go/bin (ensure it's in your PATH).

  • Windows (amd64):

    New-Item -ItemType Directory -Path $HOME\go\bin -Force | Out-Null; iwr (iwr https://api.github.com/repos/nicholas-fedor/shoutrrr/releases/latest | ConvertFrom-Json).assets.where({$_.name -like "*windows_amd64*.zip"}).browser_download_url -OutFile shoutrrr.zip; Add-Type -AssemblyName System.IO.Compression.FileSystem; ($z=[System.IO.Compression.ZipFile]::OpenRead("$PWD\shoutrrr.zip")).Entries | ? {$_.Name -eq 'shoutrrr.exe'} | % {[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$HOME\go\bin\$($_.Name)", $true)}; $z.Dispose(); rm shoutrrr.zip; if (Test-Path "$HOME\go\bin\shoutrrr.exe") { Write-Host "Successfully installed shoutrrr.exe to $HOME\go\bin" } else { Write-Host "Failed to install shoutrrr.exe" }
    
  • Linux (amd64):

     mkdir -p $HOME/go/bin && curl -L $(curl -s https://api.github.com/repos/nicholas-fedor/shoutrrr/releases/latest | grep -o 'https://[^"]*linux_amd64[^"]*\.tar\.gz"' | grep -o 'https://[^"]*' | head -n1) | tar -xz -C $HOME/go/bin shoutrrr
    
  • macOS (amd64):

     mkdir -p $HOME/go/bin && curl -L $(curl -s https://api.github.com/repos/nicholas-fedor/shoutrrr/releases/latest | grep -o 'https://[^"]*macOS_amd64[^"]*\.tar\.gz"' | grep -o 'https://[^"]*' | head -n1) | tar -xz -C $HOME/go/bin shoutrrr
    

[!Note] Visit the releases page for other architectures (e.g., arm, arm64, i386, riscv64).

Container Images

  • Docker Hub:

    docker pull nickfedor/shoutrrr:latest
    
  • GHCR:

    docker pull ghcr.io/nicholas-fedor/shoutrrr:latest
    

[!Note] Tags: latest (stable), vX.Y.Z (specific version), nightly (development), platform-specific (e.g., amd64-nightly).

Go Package

go get github.com/nicholas-fedor/shoutrrr@latest

Minimum Supported Version Policy

Projects importing Shoutrrr are expected to follow the latest Go minor and/or patch semantic version; ergo, Shoutrrr follows the latest minor Go version, i.e. 1.27.

GitHub Action

- name: Shoutrrr
  uses: nicholas-fedor/shoutrrr-action@v1
  with:
    url: ${{ secrets.SHOUTRRR_URL }}
    title: Deployed ${{ github.sha }}
    message: See changes at ${{ github.event.compare }}.

Usage

CLI

shoutrrr send --url "slack://hook:T00000000-B00000000-XXXXXXXXXXXXXXXXXXXXXXXX@webhook" --message "Hello, Slack!"

Go Package Usage

import "github.com/nicholas-fedor/shoutrrr"

errs := shoutrrr.Send("slack://hook:T00000000-B00000000-XXXXXXXXXXXXXXXXXXXXXXXX@webhook", "Hello, Slack!")
if len(errs) > 0 {
    // Handle errors
}

Docker

docker run --rm nickfedor/shoutrrr:latest send --url "slack://hook:T00000000-B00000000-XXXXXXXXXXXXXXXXXXXXXXXX@webhook" --message "Hello, Slack!"

GitHub Action Usage

See installation example above.

Use as a Package

Option 1 - Using the direct send command

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

Option 2 - Using a sender

Single URL
url := "slack://token-a/token-b/token-c"
sender, err := shoutrrr.CreateSender(url)
params := types.Params{}
sender.Send("Hello world (or slack channel) !", &params)
Multiple URLs
urls := []string {
  "slack://token-a/token-b/token-c"
  "discord://token@channel"
}
sender, err := shoutrrr.CreateSender(urls...)
params := types.Params{}
sender.Send("Hello world (or slack channel) !", &params)
Custom HTTP Client (SSRF / Egress Control)
import (
    "crypto/tls"
    "log"
    "net/http"

    "github.com/nicholas-fedor/shoutrrr"
    "github.com/nicholas-fedor/shoutrrr/pkg/types"
)

customClient := &http.Client{
    Transport: &http.Transport{
        TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS12},
    },
}

sender, err := shoutrrr.NewSenderWithOptions(
    nil,
    types.SenderOptions{HTTPClient: customClient},
    "discord://token@channel",
)
if err != nil {
    log.Fatal(err)
}
sender.Send("Hello with custom egress!", nil)
Message Levels

Services that support severity or priority can receive a semantic level through the level param.

Shoutrrr defines five levels: Unknown, Debug, Info, Warning, and Error. The default is Info.

params := types.Params{}
params.SetLevel(types.Warning)
params.SetTitle("Disk usage high")

errs := sender.Send("Root partition at 92%", &params)

Services that implement types.RichSender (such as Discord) receive the full []types.MessageItem slice when using SendItems, preserving level, fields, and file attachments.

Services that do not implement RichSender fall back to plain text automatically.

Per-Target Errors

Sender.Send, *ServiceRouter.SendAsync, and *ServiceRouter.SendItems return one error per unique configured target, in the deduplicated target order produced by CreateSender. Each error is wrapped in *types.TargetError, which carries the service URL/ID and supports errors.Unwrap, errors.Is, and errors.As:

errs := sender.Send("deploy complete", nil)
for i, err := range errs {
    if err == nil {
        continue
    }
    var targetErr *types.TargetError
    if errors.As(err, &targetErr) {
        log.Printf("failed to send to %s: %v", targetErr.URL, targetErr.Err)
    }
}
Context Propagation

Services that implement types.ContextSender or types.ContextAttachmentSender receive a context.Context derived from the router's base context with a per-service timeout.

This enables cancellation and deadline propagation without changing the existing Sender or RichSender contracts.

Format Conversion
import "github.com/nicholas-fedor/shoutrrr/pkg/format"

body, err := format.ConvertFormat("Hello **world**", "markdown", "text")

Supported conversions: textmarkdownhtml.

Use Through the CLI

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

Use as a GitHub Action

You can also use Shoutrrr in a GitHub Actions workflow.

name: Deploy
on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - [Your other workflow steps]

      - name: Shoutrrr
        uses: nicholas-fedor/shoutrrr-action@v0.0.11
        with:
          url: ${{ secrets.SHOUTRRR_URL }}
          title: Deployed ${{ github.sha }}
          message: See changes at ${{ github.event.compare }}.

Supported Services

ServiceDescription
BarkiOS push notifications
DiscordDiscord webhooks
GenericCustom HTTP webhooks
Google ChatGoogle Chat webhooks
GotifyGotify push notifications
IFTTTIFTTT webhooks
JoinJoin push notifications
LarkLark (Feishu) webhooks
LoggerLocal logging (for testing)
MatrixMatrix rooms
MattermostMattermost webhooks
MQTTMQTT message broker
NotifiarrNotifiarr message forwarding
NtfyNtfy push notifications
OpsgenieOpsgenie alerts
PagerDutyPagerDuty incident notifications
PushbulletPushbullet push notifications
PushoverPushover push notifications
Rocket.ChatRocket.Chat webhooks
SignalgridSignalgrid push notifications
SlackSlack webhooks or Bot API
SMTPEmail notifications
TeamsMicrosoft Teams webhooks
TelegramTelegram bots
TwilioTwilio SMS notifications
ZulipZulip chat
XMPPXMPP messages (if enabled)

Service Discovery

Use services.SupportedSchemas() and services.SupportsSchema(schema) to enumerate or check available notification services without constructing a router:

import "github.com/nicholas-fedor/shoutrrr/pkg/services"

for _, schema := range services.SupportedSchemas() {
    fmt.Println(schema)
}

if services.SupportsSchema("discord") {
    // ...
}

Contributors ✨

Thanks goes to these wonderful people (emoji key):

This project follows the all-contributors specification. Contributions of any kind welcome!

  • Watchtower - Automate Docker container image updates.
  • Shoutrrr GitHub Action - Notifications using Shoutrrr in GitHub Actions.
  • Bezel - A lightweight server monitoring platform that includes Docker statistics, historical data, and alert functions.
  • WatchYourLAN - Lightweight network IP scanner with web GUI.
  • DNSControl- Infrastructure as code for DNS.
  • docker-volume-backup - Backup Docker volumes locally or to any S3, WebDAV, Azure Blob Storage, Dropbox, Google Drive or SSH compatible storage.
  • BirdNET-Go - An AI solution for continuous avian monitoring and identification.

Contributors

(top 30 of 37)

renovate[bot]

854 commits

nicholas-fedor

267 commits

piksel

165 commits

nicholas-fedor/shoutrrr

A notification library for gophers and their furry friends.

174

stars

1,741

commits

Go

primary language

Sep 9, 2026

updated

shoutrrr.nickfedor.com/
cli
go
golang
library
notification
spf13-cobra

README

Shoutrrr

A notification library for gophers and their furry friends.
Heavily inspired by caronc/apprise.

OpenSSF Scorecard codecov Codacy Badge github code size in bytes Pulls from DockerHub go.dev reference Ask DeepWiki All Contributors

license

Table of Contents

Full Documentation

Visit the project's GitHub Page for full documentation.

Installation

From Source

go install github.com/nicholas-fedor/shoutrrr/shoutrrr@latest

Binaries

Install the latest release binary to $HOME/go/bin (ensure it's in your PATH).

  • Windows (amd64):

    New-Item -ItemType Directory -Path $HOME\go\bin -Force | Out-Null; iwr (iwr https://api.github.com/repos/nicholas-fedor/shoutrrr/releases/latest | ConvertFrom-Json).assets.where({$_.name -like "*windows_amd64*.zip"}).browser_download_url -OutFile shoutrrr.zip; Add-Type -AssemblyName System.IO.Compression.FileSystem; ($z=[System.IO.Compression.ZipFile]::OpenRead("$PWD\shoutrrr.zip")).Entries | ? {$_.Name -eq 'shoutrrr.exe'} | % {[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$HOME\go\bin\$($_.Name)", $true)}; $z.Dispose(); rm shoutrrr.zip; if (Test-Path "$HOME\go\bin\shoutrrr.exe") { Write-Host "Successfully installed shoutrrr.exe to $HOME\go\bin" } else { Write-Host "Failed to install shoutrrr.exe" }
    
  • Linux (amd64):

     mkdir -p $HOME/go/bin && curl -L $(curl -s https://api.github.com/repos/nicholas-fedor/shoutrrr/releases/latest | grep -o 'https://[^"]*linux_amd64[^"]*\.tar\.gz"' | grep -o 'https://[^"]*' | head -n1) | tar -xz -C $HOME/go/bin shoutrrr
    
  • macOS (amd64):

     mkdir -p $HOME/go/bin && curl -L $(curl -s https://api.github.com/repos/nicholas-fedor/shoutrrr/releases/latest | grep -o 'https://[^"]*macOS_amd64[^"]*\.tar\.gz"' | grep -o 'https://[^"]*' | head -n1) | tar -xz -C $HOME/go/bin shoutrrr
    

[!Note] Visit the releases page for other architectures (e.g., arm, arm64, i386, riscv64).

Container Images

  • Docker Hub:

    docker pull nickfedor/shoutrrr:latest
    
  • GHCR:

    docker pull ghcr.io/nicholas-fedor/shoutrrr:latest
    

[!Note] Tags: latest (stable), vX.Y.Z (specific version), nightly (development), platform-specific (e.g., amd64-nightly).

Go Package

go get github.com/nicholas-fedor/shoutrrr@latest

Minimum Supported Version Policy

Projects importing Shoutrrr are expected to follow the latest Go minor and/or patch semantic version; ergo, Shoutrrr follows the latest minor Go version, i.e. 1.27.

GitHub Action

- name: Shoutrrr
  uses: nicholas-fedor/shoutrrr-action@v1
  with:
    url: ${{ secrets.SHOUTRRR_URL }}
    title: Deployed ${{ github.sha }}
    message: See changes at ${{ github.event.compare }}.

Usage

CLI

shoutrrr send --url "slack://hook:T00000000-B00000000-XXXXXXXXXXXXXXXXXXXXXXXX@webhook" --message "Hello, Slack!"

Go Package Usage

import "github.com/nicholas-fedor/shoutrrr"

errs := shoutrrr.Send("slack://hook:T00000000-B00000000-XXXXXXXXXXXXXXXXXXXXXXXX@webhook", "Hello, Slack!")
if len(errs) > 0 {
    // Handle errors
}

Docker

docker run --rm nickfedor/shoutrrr:latest send --url "slack://hook:T00000000-B00000000-XXXXXXXXXXXXXXXXXXXXXXXX@webhook" --message "Hello, Slack!"

GitHub Action Usage

See installation example above.

Use as a Package

Option 1 - Using the direct send command

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

Option 2 - Using a sender

Single URL
url := "slack://token-a/token-b/token-c"
sender, err := shoutrrr.CreateSender(url)
params := types.Params{}
sender.Send("Hello world (or slack channel) !", &params)
Multiple URLs
urls := []string {
  "slack://token-a/token-b/token-c"
  "discord://token@channel"
}
sender, err := shoutrrr.CreateSender(urls...)
params := types.Params{}
sender.Send("Hello world (or slack channel) !", &params)
Custom HTTP Client (SSRF / Egress Control)
import (
    "crypto/tls"
    "log"
    "net/http"

    "github.com/nicholas-fedor/shoutrrr"
    "github.com/nicholas-fedor/shoutrrr/pkg/types"
)

customClient := &http.Client{
    Transport: &http.Transport{
        TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS12},
    },
}

sender, err := shoutrrr.NewSenderWithOptions(
    nil,
    types.SenderOptions{HTTPClient: customClient},
    "discord://token@channel",
)
if err != nil {
    log.Fatal(err)
}
sender.Send("Hello with custom egress!", nil)
Message Levels

Services that support severity or priority can receive a semantic level through the level param.

Shoutrrr defines five levels: Unknown, Debug, Info, Warning, and Error. The default is Info.

params := types.Params{}
params.SetLevel(types.Warning)
params.SetTitle("Disk usage high")

errs := sender.Send("Root partition at 92%", &params)

Services that implement types.RichSender (such as Discord) receive the full []types.MessageItem slice when using SendItems, preserving level, fields, and file attachments.

Services that do not implement RichSender fall back to plain text automatically.

Per-Target Errors

Sender.Send, *ServiceRouter.SendAsync, and *ServiceRouter.SendItems return one error per unique configured target, in the deduplicated target order produced by CreateSender. Each error is wrapped in *types.TargetError, which carries the service URL/ID and supports errors.Unwrap, errors.Is, and errors.As:

errs := sender.Send("deploy complete", nil)
for i, err := range errs {
    if err == nil {
        continue
    }
    var targetErr *types.TargetError
    if errors.As(err, &targetErr) {
        log.Printf("failed to send to %s: %v", targetErr.URL, targetErr.Err)
    }
}
Context Propagation

Services that implement types.ContextSender or types.ContextAttachmentSender receive a context.Context derived from the router's base context with a per-service timeout.

This enables cancellation and deadline propagation without changing the existing Sender or RichSender contracts.

Format Conversion
import "github.com/nicholas-fedor/shoutrrr/pkg/format"

body, err := format.ConvertFormat("Hello **world**", "markdown", "text")

Supported conversions: textmarkdownhtml.

Use Through the CLI

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

Use as a GitHub Action

You can also use Shoutrrr in a GitHub Actions workflow.

name: Deploy
on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - [Your other workflow steps]

      - name: Shoutrrr
        uses: nicholas-fedor/shoutrrr-action@v0.0.11
        with:
          url: ${{ secrets.SHOUTRRR_URL }}
          title: Deployed ${{ github.sha }}
          message: See changes at ${{ github.event.compare }}.

Supported Services

ServiceDescription
BarkiOS push notifications
DiscordDiscord webhooks
GenericCustom HTTP webhooks
Google ChatGoogle Chat webhooks
GotifyGotify push notifications
IFTTTIFTTT webhooks
JoinJoin push notifications
LarkLark (Feishu) webhooks
LoggerLocal logging (for testing)
MatrixMatrix rooms
MattermostMattermost webhooks
MQTTMQTT message broker
NotifiarrNotifiarr message forwarding
NtfyNtfy push notifications
OpsgenieOpsgenie alerts
PagerDutyPagerDuty incident notifications
PushbulletPushbullet push notifications
PushoverPushover push notifications
Rocket.ChatRocket.Chat webhooks
SignalgridSignalgrid push notifications
SlackSlack webhooks or Bot API
SMTPEmail notifications
TeamsMicrosoft Teams webhooks
TelegramTelegram bots
TwilioTwilio SMS notifications
ZulipZulip chat
XMPPXMPP messages (if enabled)

Service Discovery

Use services.SupportedSchemas() and services.SupportsSchema(schema) to enumerate or check available notification services without constructing a router:

import "github.com/nicholas-fedor/shoutrrr/pkg/services"

for _, schema := range services.SupportedSchemas() {
    fmt.Println(schema)
}

if services.SupportsSchema("discord") {
    // ...
}

Contributors ✨

Thanks goes to these wonderful people (emoji key):

This project follows the all-contributors specification. Contributions of any kind welcome!

  • Watchtower - Automate Docker container image updates.
  • Shoutrrr GitHub Action - Notifications using Shoutrrr in GitHub Actions.
  • Bezel - A lightweight server monitoring platform that includes Docker statistics, historical data, and alert functions.
  • WatchYourLAN - Lightweight network IP scanner with web GUI.
  • DNSControl- Infrastructure as code for DNS.
  • docker-volume-backup - Backup Docker volumes locally or to any S3, WebDAV, Azure Blob Storage, Dropbox, Google Drive or SSH compatible storage.
  • BirdNET-Go - An AI solution for continuous avian monitoring and identification.

Contributors

(top 30 of 37)

renovate[bot]

854 commits

nicholas-fedor

267 commits

piksel

165 commits

Languages

Go

97.6%

Shell

1.9%