# gotify/cli: pushing notifications to a self hosted Gotify server

> The Gotify command line client stores a token and server URL in a config file, adds an init wizard, accepts piped input, and can watch a command and push only what changed, which is the part that is more than a curl wrapper.

**gotify/cli** — A command line interface for pushing messages to gotify/server.

- Repository: https://github.com/gotify/cli
- Stars: 595 · Forks: 75
- Language: Go
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/gotify-cli

## A client for one Gotify server, and the project says it is optional

Gotify-CLI is a command line client for pushing messages to gotify/server. The documentation is unusually direct about its own necessity: it is not required to push messages, and there is an alternatives section pointing at curl and HTTPie.

That framing is worth taking at face value, because it tells you exactly what the client is. Gotify's server exposes an HTTP endpoint for creating a message, and any HTTP client can call it. The CLI is a convenience wrapper that stores credentials, generates the request, and adds a few things raw HTTP cannot do easily.

The feature list is short. It stores the token and the URL in a config file, it has an initialization wizard, it supports piping so a message can be fed in on standard input, it is simple to use, and it can watch a script and push the result when it changes.

The implementation is Go, the module path is github.com/gotify/cli/v2, and the code is a small tree: cli.go at the top with a command directory, a config directory and a utils directory. It uses the official gotify/go-api-client/v2 for the API, urfave/cli.v1 for the command line parsing, adrg/xdg for the config location, and go-openapi/runtime behind the API client.

## Four config file locations, and the token type that matters

Before anything works, the client needs a token and a URL, and the documentation is precise about where those can live.

The config file is searched for in four places, in this order: /etc/gotify/cli.json, then $XDG_CONFIG_HOME/gotify/cli.json, then ~/.gotify/cli.json, and finally ./cli.json in the working directory. That last entry matters for scripts, because a config in the current directory overrides everything else, and it is also the mechanism the container example relies on.

The structure is a small JSON object.

```json
{
  "token": "A4ZudDRdLT40L5X",
  "url": "https://gotify.example.com",
  "defaultPriority": 6
}
```

The token field is an application token, and the documentation states in the table that a client token will not work. That is a detail worth internalising, because a Gotify installation has both kinds of token, and the wrong one produces a failure that looks like a network problem rather than a permission problem.

The url field is the address of your Gotify server, and defaultPriority sets the priority for messages when the field is absent it is set to zero. Priority matters on a phone, because the Gotify clients use it to decide whether a message interrupts.

There is also a way to override the token and the URL per invocation, through the --token and --url options, with the token option also reading the GOTIFY_TOKEN environment variable. For a CI job, that is usually cleaner than writing a config file into the workspace.

## Installing the binary and running the init wizard

The manual install is a download, a chmod and a version check. The example in the documentation uses version v2.2.0, and the release assets are named per platform.

```bash
$ wget -O gotify https://github.com/gotify/cli/releases/download/v2.2.0/gotify-cli-linux-amd64
# or
$ curl -Lo gotify https://github.com/gotify/cli/releases/download/v2.2.0/gotify-cli-linux-amd64
```

The file has to be made executable.

```bash
$ chmod +x gotify
```

Then the version command confirms you downloaded something your system can run, and the documentation is clear that if it does not work you may have downloaded the wrong file or your device is not supported. The version output shows the version, the commit and the build date, which is also how you confirm which build you are running.

For a first use, the wizard is the quickest route.

```bash
$ gotify init
```

When the wizard finishes you are ready to push. A manual config file is the alternative, and the same set of commands covers the usual shapes of a message: a bare argument, a quoted argument, a pipe, a redirect from a file, a title and priority, and the watch command.

The packaging routes are AUR and Homebrew. The AUR package is gotify-cli and installs with any AUR helper, and the Homebrew formula is gotify. Both carry a note that maintenance of the package is not performed by the Gotify team, which is worth knowing before you rely on a distribution package for a security relevant tool.

## The command surface is init, config, push and watch

The global help output is the fastest way to understand the tool, and it is small. There are four commands besides help and version: init, which initializes the CLI, config, which shows the config, push, which pushes a message, and watch, which watches the result of a command and pushes the output difference.

The push command takes a message text and offers a priority and a title, a quiet mode that prints nothing on success, a content type that points at the Gotify message extras documentation for client display, and a switch to disable evaluating backslash escapes so that a literal backslash n is sent as two characters rather than a newline. It also accepts the message on standard input, which is what makes it compose with other tools.

The watch command is the reason the tool exists rather than being replaced by a curl line. It takes a command, runs it on an interval that defaults to two seconds, and pushes the difference in the output rather than the whole output each time. The command is passed to exec with a default of sh -c, so a pipeline works as a single argument, the title defaults to the command itself, and the output verbosity can be short, default or long.

Two environment variables cover the cases where TLS gets in the way. GOTIFY_SKIP_VERIFY_TLS disables certificate validation, and SSL_CERT_FILE points at a custom root certificate so a self signed server certificate can be verified properly instead of skipped.

## Building for five architectures and running it as a container

The Makefile in the repository is where the release engineering is visible, and it covers both the binaries and the container image.

The image build target uses docker buildx and defaults to a multi platform list of linux/amd64, linux/arm64, linux/386, linux/arm/v7 and linux/riscv64, with the registry, image name and platform all overridable as environment variables. The tag set depends on whether a version was supplied, with master always present and latest, the full version and the shortened version tags added when a version is given.

```bash
$ gotify push my message
$ gotify push "my message"
$ echo my message | gotify push
$ gotify push < somefile
$ gotify push -t "my title" -p 10 "my message"
$ gotify watch "curl http://example.com/api | jq '.data'"
```

The Go version used in the build is derived from the toolchain line in the module file rather than hard coded, and the build flags inject the version, the build date and the commit into the binary so that the version command reports them. The set of push invocations the tool is designed around is short enough to list in full, from a bare argument to a title and priority, and the watch form that pipes a command through jq.

The Dockerfile in the docker directory contains the steps to build a new version of the CLI and run it in a minimal Alpine container. The documented run command mounts your config file into the container and passes a message.

```bash
docker run -it -v "$PWD/cli.json:/home/app/cli.json" gotify/gotify-cli:latest push -p 5 "Test from Gotify CLI"
```

The volume mount is the same ./cli.json lookup path as the local one, which is a neat consistency: the container finds its configuration in the working directory exactly as the binary does.

## Where the Gotify CLI is the wrong tool

The project itself sets the first boundary, so start there. Any HTTP client can push a message, and the documentation shows the two line curl form, and the HTTPie equivalent. If you need exactly one message from one script, that is the whole solution and it has no dependencies at all.

The CLI earns its place in three situations. When several scripts on a machine all need the same server and token, because the config file removes the duplication. When a message is piped from another program, because the standard input handling is built in. And when you want to watch something and be told only when it changes, because writing that correctly with a shell loop and a diff is work you would otherwise repeat.

Beyond that, the tool has real limits. There is no support for more than one server, since the config holds a single url and a single token, so a machine that talks to two Gotify instances needs two config files and some care about which one is picked up from the working directory. The client is also not a library, so an application that wants to send notifications should use the Go API client directly rather than shelling out.

One more is worth stating plainly. The version command is how you check what you have, and the help text reproduced in the documentation still shows version 2.2.0 and an older set of output formats, while the current release is 2.4.0. The documentation is not in step with the binary, so trust your own version output over the examples.

## Licence, release history and the pace of changes

The licence is MIT, with a LICENSE file at the root of the repository, so the code can be used, modified and redistributed in commercial and closed products with the notice retained. That is a permissive grant and this is not legal advice, but for a small Go client it is about as uncomplicated as it gets.

The release history shows two distinct periods. Versions 2.3.1 and 2.3.2 were both published on 2024-08-01, minutes apart, and then version 2.4.0 arrived on 2026-07-21, which is also the date of the last push to the default branch. The repository is not archived.

That gap is the fact to plan around. A client that ships a version, sits still for two years, and then ships a new release is a low frequency project, and the cost of that is on the person who tries to build a workflow on top of it. There is a mismatch to plan for, since the module declares Go 1.25 with a toolchain of go1.26.0, so a recent Go toolchain is needed to build from source even though the released binary is self contained.

The dependency set is small and sensible for what the tool does: the official Gotify API client, the CLI framework, the XDG helper for config lookup, a spinner for progress output, and the crypto package. The bulk of the go-openapi indirect dependencies come from the API client, which is generated, and that is a normal shape for a tool that wraps a REST API.

## Conclusion

gotify/cli fits a self hosted Gotify setup where a script, a cron job or a monitoring check should send a notification without hand building HTTP requests, and its watch command is the reason to prefer it over a raw curl call. It does not fit a team that needs to talk to several Gotify servers, to embed notifications inside an application, or to run on a platform the release artefacts do not cover. Before adopting, confirm the token you use is an application token rather than a client token, check the config file location that your environment will read, and read the version output of the binary you install, because the help text in the documentation still shows an older release.

## FAQ

### How do I install the Gotify command line client?

Download the release asset for your platform from the releases page, make it executable with chmod +x and run gotify version to confirm it works. On Arch Linux it is in the AUR as gotify-cli and on macOS it is in Homebrew as gotify, and the documentation notes that maintenance of both packages is not performed by the Gotify team.

### Where does the Gotify CLI look for its configuration file?

It searches /etc/gotify/cli.json, then $XDG_CONFIG_HOME/gotify/cli.json, then ~/.gotify/cli.json, and finally ./cli.json in the current working directory. The file holds a token, which must be an application token rather than a client token, a url and an optional defaultPriority.

### What does the gotify watch command do?

It watches the result of a command and pushes the output difference. The interval defaults to two seconds, the command is passed to exec with a default of sh -c, the title defaults to the command itself, and the output verbosity can be short, default or long.

### Do I need the Gotify CLI to send a notification?

No. The documentation states that the client is not required to push messages and shows the equivalent curl and HTTPie commands, so any HTTP client works. The CLI adds a stored config, an init wizard, piped input and the watch command.

### How do I use a self signed certificate with the Gotify CLI?

The documentation describes two options: an environment variable that disables TLS certificate validation, and setting SSL_CERT_FILE to a custom root certificate so a self signed server certificate can be verified properly instead of skipped.

## Sources

- [gotify/cli on GitHub](https://github.com/gotify/cli)
- [Issues](https://github.com/gotify/cli/issues)
- [License: MIT](https://github.com/gotify/cli/blob/master/LICENSE)
- [README](https://github.com/gotify/cli/blob/master/README.md)
- [Releases](https://github.com/gotify/cli/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/gotify-cli
