# appleboy/gorush: a Go push notification server for APNs, FCM and HMS

> gorush is a Gin-based micro server that takes one HTTP request and fans it out to Apple, Firebase and Huawei push services. It is for teams that would rather run a small Go binary than wire three vendor SDKs into their application code.

**appleboy/gorush** — A push notification server written in Go (Golang).

- Repository: https://github.com/appleboy/gorush
- Stars: 8,778 · Forks: 887
- Language: Go
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/appleboy-gorush

## The problem gorush solves for multi-platform app teams

An app that ships on iOS, Android and Huawei devices talks to three different push services, each with its own authentication, payload shape and error vocabulary. APNs wants an HTTP/2 connection and a p8, p12 or pem certificate. FCM wants a service account. HMS has its own push kit. Writing that inside the application means three SDKs, three retry policies and three sets of credentials living in the same process that serves user traffic.

gorush moves that work into a separate process. The application sends one JSON document to one endpoint and gets a response back; gorush owns the connections to Apple, Google and Huawei. The README describes it as a push notification micro server built on the Gin framework, and the platform codes are explicit: 1 is iOS over APNS, 2 is Android over FCM, 3 is Huawei over HMS. The intended user is a backend team that already runs Go services and does not want push credentials scattered across application code.

## Inside the server: queue, workers and storage

The request path is short. A POST to /api/push carries a notifications array; each entry has tokens, a platform number and the message fields. From there the notification enters a queue and a pool of workers drains it toward the vendor service. The core configuration exposes worker_num, where 0 means use the number of CPU cores, and queue_num, which sets the queue size. The default queue engine is a local Go channel, and the README lists NSQ, NATS and Redis streams as alternative backends.

That design explains the failure mode you should plan for. A local channel queue lives in the process, so a crash loses whatever has not been sent. Moving to NSQ, NATS or Redis streams puts the queue outside the process. The README also states that gorush supports graceful shutdown, waiting for workers and the queue to finish before the service stops, which is what makes a rolling restart survivable when the queue is in memory.

Statistics are stored through a separate choice: memory, Redis, BoltDB, BuntDB, LevelDB or BadgerDB. The README says the server exposes /api/stat/app for success and failure counts, /api/config to show the loaded YAML, and /sys/stats for response time and status code counts. Prometheus metrics can be exposed as well. The repository layout matches this: notify/, storage/, status/, metric/ and rpc/ directories sit alongside router/ and core/.

## Installing gorush and sending a first notification

The README offers several routes: an install script, a manual binary download, package managers, a build from source and Docker. The quick start uses the binary. It downloads a release asset, marks it executable and starts the server on the default port 8088.

```bash
wget https://github.com/appleboy/gorush/releases/download/v1.18.9/gorush-1.18.9-linux-amd64 -O gorush
chmod +x gorush
./gorush
```

With the server running, the first real use is a POST to /api/push. The README's example sends to platform 2, which is Android over FCM, with a single token.

```bash
curl -X POST http://localhost:8088/api/push \
  -H "Content-Type: application/json" \
  -d '{
    "notifications": [{
      "tokens": ["your_device_token"],
      "platform": 2,
      "title": "Hello World",
      "message": "Your first notification!"
    }]
  }'
```

The response reports the result of the send, and the counters behind /api/stat/app move. If you prefer Docker, the repository's docker-compose.yml runs the appleboy/gorush image, publishes 8088 and 9000, and passes GORUSH_CORE_QUEUE_NUM=512 as an environment variable, which is how the YAML keys are overridden in a container.

```yaml
services:
  gorush:
    image: appleboy/gorush
    restart: always
    ports:
      - "8088:8088"
      - "9000:9000"
    environment:
      - GORUSH_CORE_QUEUE_NUM=512
```

Configuration itself is YAML. The README's basic block sets port, worker_num, queue_num and mode under a core key. Building from source uses the Makefile, which defaults to the sqlite build tag and writes the binary into release/.

## Where gorush is the wrong tool

gorush is a delivery layer. It does not decide who should receive a notification. There is no scheduler in the README's feature list, no audience segmentation, no template versioning and no delivery-time optimization. If your product needs a campaign that fires at 9am in each user's timezone, that logic belongs in your application or in a service built for it; gorush will send whatever you hand it, when you hand it over.

The operational cost is real too. You hold the APNs certificate or p8 key, the FCM credentials and the HMS credentials, and you rotate them. The README lists HTTP, HTTPS and SOCKS5 proxy support, which matters in restricted networks, but proxy configuration is another thing you own.

There is also a version detail worth noticing. The quick start pins the download to v1.18.9 even though the recent releases listed for the repository are v1.22.0, v1.21.5 and v1.21.4. Anyone copying that command gets an older binary than the project's current release. Check the release page rather than trusting the snippet. The last push to the repository was on 2026-07-25, and v1.22.0 was tagged the same day.

## gorush compared with using the vendor SDKs directly

The alternative most teams weigh is calling the vendor libraries from their own service. The go.mod file shows what that means for Go: firebase.google.com/go/v4 for FCM, sideshow/apns2 for APNs, and a go-hms-push module that gorush redirects to a fork. Those are the same libraries gorush uses. Adopting gorush does not remove them from your stack; it moves them behind a process boundary.

The difference is where the retry loop and the credentials live. Sending directly keeps the call in your request path, so a slow APNs response becomes your latency unless you add a queue yourself. gorush gives you the queue, the worker pool, the retry-on-failure behavior and the counters as configuration rather than code. The price is an extra network hop, a second service to deploy and monitor, and a REST or gRPC contract to keep in sync.

For a single-platform app with modest volume, direct SDK calls are simpler and there is no reason to run another process. gorush starts to pay off when the second platform arrives, because the application stops caring which vendor a token belongs to.

## Licence and the cost of keeping gorush current

gorush is MIT licensed, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are kept. That is a permissive licence with no copyleft obligation on your own code. It says nothing about the licences of the dependencies, and go.mod pulls in a long list including Gin, gRPC, Prometheus client libraries, Badger, LevelDB and several storage drivers. Anyone redistributing a built binary should review those separately; this is not legal advice.

Upgrade cost is dominated by the vendor side. APNs, FCM and HMS change their APIs, and the libraries gorush depends on track those changes. The project pins specific versions in go.mod, so a dependency bump is a deliberate act. The repository also carries .goreleaser.yaml, a Trivy configuration and a daily security scan workflow, which suggests releases are built and scanned rather than tagged by hand. The Makefile's default sqlite build tag means the binary you build locally may differ from the released artifact unless you match the build flags.

## Deployment targets and interfaces beyond HTTP

The README lists Docker, Kubernetes, AWS Lambda and Netlify Functions as deployment options, with helm/ and k8s/ directories in the repository. For Lambda the project depends on apex/gateway, which adapts the Go handler to the API Gateway event shape. That is a genuine difference from a long-running server: on Lambda the in-process channel queue has a very short lifetime, so an external queue backend matters more, not less.

Alongside REST, gorush exposes a gRPC service, and the repository has an rpc/ directory. The README also documents command line notifications for sending a single Android or iOS notification without going through the HTTP API, which is useful for smoke testing credentials before wiring an application to the server. TLS certificates can be obtained automatically through Let's Encrypt, and the README notes support for HTTP/2 as well as HTTP/1.1. The README states an average memory usage of about 28MB, with throughput depending on the worker and queue settings you choose.

## Conclusion

Adopt gorush if you already send to more than one push platform and want the vendor SDKs behind one HTTP or gRPC endpoint, and if you are willing to own the APNs certificate or p8 key and the FCM and HMS credentials yourself. Do not adopt it if you need per-user scheduling, deep template logic or a hosted dashboard; gorush is a delivery layer, not a campaign tool. Before rolling it out, verify your /api/push responses against a real device on each platform code you use, confirm the queue backend and storage engine you configure survive a restart, and check that your config.yml holds the certificate paths the binary expects.

## FAQ

### What is gorush?

gorush is a push notification micro server written in Go and built on the Gin framework. It accepts notifications over a REST API or gRPC and delivers them to Apple Push Notification service, Firebase Cloud Messaging or Huawei Push Service, with platform codes 1, 2 and 3 respectively.

### Which push platforms does gorush support?

The README lists APNS for iOS, FCM for Android and HMS for Huawei devices, using the apns2, go-fcm and go-hms-push libraries. Platform codes are 1 for iOS, 2 for Android and 3 for Huawei.

### How do I install gorush?

The README gives several routes: an install script, a manual binary download from the releases page, package managers, a build from source using the Makefile, and a Docker image published as appleboy/gorush. The quick start downloads a release binary, marks it executable and runs it on port 8088.

### What queue backends can gorush use?

The default queue engine is a local Go channel. The README also lists NSQ, NATS and Redis streams as alternative backends, configured through the core section of the YAML file or through environment variables such as GORUSH_CORE_QUEUE_NUM.

### Where does gorush store notification statistics?

Statistics can be kept in memory, Redis, BoltDB, BuntDB, LevelDB or BadgerDB. The server exposes /api/stat/app for success and failure counts, /api/config to show the loaded YAML configuration and /sys/stats for response time and status code counts.

## Sources

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

---

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