Get a message when a conversion is done, with a link to the result. Any Apprise service: ntfy, Pushover, Discord, Telegram, email and more. Opt-in, no file names sent.
imgcompress sends the message through Apprise, which is built into the container. Set NOTIFY_URL to any Apprise URL for the service you like — that's the only change you need.
I use ntfy (self-hosted) and that's the worked example below. It's a choice, not a requirement. Pushover, Discord, Telegram, email and many more work the same way: swap the URL.
💡HTTPS for the result link
I highly recommend HTTPS for imgcompress (for example https://imgcompress.lan), so the link in the message is secure. See Production setup. If you also self-host ntfy, put both behind the same reverse proxy.
Auth and tokens go inside the URL. Treat NOTIFY_URL as a secret — keep it in .env, never commit it. imgcompress never writes it to the logs.
The container needs network access to the service. On an internal-only network (like proxy-no-internet), only an internal ntfy (or similar) works; cloud services need outbound reach.
?priority=high is ntfy-specific. Other services have their own options in the Apprise docs.
I test ntfy. Other services use the same Apprise URL mechanism; I haven't run them myself.
URL-encode special characters in passwords (%40 for @).
All hostnames on this page (imgcompress.lan, notify.lan) are just examples. Use your own.
The diagram shows the ntfy path (what I run). With Pushover, Discord, Telegram or email, Apprise talks straight to that service instead of ntfy.
flowchart LR
PC["Browser on your PC<br/>starts the job"] --> IMG["imgcompress container"]
IMG -- "NOTIFY_URL<br/>(built-in Apprise)" --> N["ntfy<br/>notify.lan"]
N -- "push" --> PH["ntfy app<br/>on your phone"]
N -. "iPhone only: wake-up<br/>(message ID, no text)" .-> UP["ntfy.sh"]
UP -. "Apple push" .-> PH
PH -- "tap link<br/>(PUBLIC_BASE_URL)" --> IMG
You start a conversion and turn on Notify me when done.
When it finishes, the container sends one short message through Apprise to NOTIFY_URL.
Your service delivers it (here: ntfy → phone app). On an iPhone with self-hosted ntfy, ntfy.sh only wakes the app; the app fetches the message from your server.
The message links back to your container at PUBLIC_BASE_URL. The link opens that one result on any device.
The address you open imgcompress at, like https://imgcompress.lan. With it, the message links to the result. It's never taken from a request, because the Host header can be faked.
TZ
not set (UTC)
Container time zone for the "Link expires …" line, like Europe/Berlin.
A failed message is logged (without the URL) and never breaks a conversion.
Subscribe to the topic. In the app, tap +, enter your topic, turn on Use another server, and enter https://notify.lan.
Configure imgcompress. Add this to the environment: section:
yaml
environment:
# ntfys:// = HTTPS, ntfy:// = plain HTTP. priority=high makes the phone ring.
- NOTIFY_URL=ntfys://notify.lan/imgc-7f3k9q2xw8?priority=high
# The address you open imgcompress at. Adds the link to the result.
- PUBLIC_BASE_URL=https://imgcompress.lan
# Clock for "Link expires …".
- TZ=Europe/Berlin
No reverse proxy yet? Use ntfy://notify.lan:8080/… in NOTIFY_URL, http://notify.lan:8080 in the app, and http://imgcompress.lan:3001 for PUBLIC_BASE_URL.
Restart with docker compose up -d.
Test it. Start a conversion, turn on Notify me when done, and close the tab. When it's done, your phone shows something like this:
text
3 images ready in 47 min 35 s
Open this link to download them:
https://imgcompress.lan/r/8f3c…/Xq7v…
Link expires 7 Nov 2026, 18:51 CET.
Tap the link to open the result. To test ntfy alone: curl -d "Hello" https://notify.lan/imgc-7f3k9q2xw8.
💡Nothing arrives?
Check that both the phone and the imgcompress container can reach your notify host, and that the topic (or credentials) match NOTIFY_URL. docker logs imgcompress shows a failed message (without the URL).
The switch: it only shows when NOTIFY_URL is set. It's off until you turn it on once; then your browser remembers it. Nothing is stored in the container, so other users aren't affected.
The message: how many images are ready and how long it took. Never file names or images. If the job fails, it says so and links to the error. It uses the language your imgcompress page was in.
The link: a private link to this one result. It works on any number of devices and has no download limit. It expires when the result is cleaned up (48 hours by default; see How long results are kept). The message shows that as a date and time in the container's TZ.
Without PUBLIC_BASE_URL: no link. The message tells you which browser and device to open, like "Open imgcompress in Chrome on your Windows PC."
The link carries its own secret key, separate from the job's key, and the container stores only hashes of both. More in Background jobs.
⚠️Keep your topic private
Anyone who can read your topic can open the linked result until it's cleaned up, so use a topic that is hard to guess.
Your own ntfy, only on your network (like notify.lan, not reachable from the Internet): a long random topic is enough. No user or password needed.
A public or shared ntfy (ntfy.sh, or one other people use): also protect the topic with a user and password or an access token. See Access control.
The iPhone wake-up through ntfy.sh (below) only carries a message ID, so it doesn't need this on its own.
A great starting point for self-hosters. Every self-hoster uses a different approach; this is one I find simple and easy to get started with. The compose files are just for getting started — keep the layout, swap NOTIFY_URL for whatever service you prefer.
Ready-made folder on GitHub: docker/compose/homelab-traefik-ntfy/ (on release/v1.0.0). Traefik on port 80, ImgCompress with no Internet (proxy-no-internet --internal), and your own ntfy with ntfy.sh only waking the iPhone app. Short numbered steps and a diagram are in that folder's README.
This is just an example. I run this in my home lab; it's not perfect but it works for me. On my other setups I use HTTPS, but here I want to show a simple setup most self-hosters understand.
flowchart LR
subgraph Home["Home network"]
Phone["Browser / phone"]
DNS["Router DNS<br/>imgcompress.lan<br/>ntfy.lan<br/>traefik.lan"]
end
subgraph Host["Docker host"]
T["Traefik :80"]
subgraph Net["proxy-no-internet --internal"]
IC["imgcompress :5000"]
N["ntfy :80"]
end
NI["ntfy-internet"]
end
Upstream["ntfy.sh<br/>wake-up only"]
App["iPhone ntfy app"]
Phone --> DNS
DNS --> T
T -->|"Host imgcompress.lan"| IC
T -->|"Host ntfy.lan"| N
IC -->|"NOTIFY_URL<br/>ntfy://ntfy/…"| N
N --> NI
NI --> Upstream
Upstream -->|"wake"| App
App -->|"fetch message<br/>ntfy.lan<br/>(VPN when away)"| N
Minimal idea:
Setting
Example
NOTIFY_URL
ntfy://ntfy/imgcompress-<long-random>?priority=high (container name on the shared network)
PUBLIC_BASE_URL
http://imgcompress.lan
NTFY_BASE_URL
Must match the server address in the ntfy app exactly (http://ntfy.lan)
Last step: create imgcompress.lan, ntfy.lan and traefik.lan in your router's DNS settings, pointing at the Docker host. On a LAN-only ntfy, a long random topic is enough (no password).
Put imgcompress (and ntfy, if you use it) behind HTTPS as https://imgcompress.lan and https://notify.lan. Start from proxied.docker-compose.yaml (Traefik) and add ntfy on the same internal network — see Production setup. Then set NOTIFY_URL=ntfy://ntfy/… (or any other Apprise URL your network can reach) and PUBLIC_BASE_URL=https://imgcompress.lan.
.lan names need your own CA or a DNS challenge (comments in the compose file). Away from home, use a VPN (WireGuard, Tailscale).
Apple only delivers push messages through its own service, so your ntfy asks ntfy.sh to wake the iPhone app. Three ntfy settings make it work:
upstream-base-url: https://ntfy.sh (NTFY_UPSTREAM_BASE_URL): ntfy.sh wakes the app. Without it, messages can take minutes to hours.
base-url (NTFY_BASE_URL): must match the server address in the ntfy app exactly. Same scheme and host, no trailing slash.
cache-file (NTFY_CACHE_FILE) on a volume: keeps the message until the app fetches it, also after a restart.
What ntfy.sh gets: only a wake-up with the message ID. Never the text, the link or your images. The app fetches the real message straight from your ntfy. Details: ntfy iOS docs.
To allow only your own users, set NTFY_AUTH_DEFAULT_ACCESS=deny-all, create a user with ntfy user add, and put the login in the URL: ntfy://user:password@ntfy/imgc-7f3k9q2xw8. Add the same login in the ntfy app. See ntfy access control.