Self-hosted email tools usually fall into two buckets.
Some tools catch email for testing. That is where Inbucket fits.
Others help you actually manage newsletters, transactional messages, campaigns, contacts, templates, automations, and analytics. That is where Notifuse fits.
Notifuse is a modern, self-hosted emailing platform that allows you to send newsletters and transactional emails at a fraction of the cost.
The important distinction: Notifuse is not just an SMTP server. It is the email product layer you put above providers like Amazon SES, Mailgun, Postmark, Mailjet, SparkPost, SendGrid, or a generic SMTP service.
What is Notifuse?
Notifuse is a self-hosted email platform built with Go, React, and PostgreSQL.
It provides:
- Newsletter and broadcast campaigns.
- Transactional email templates.
- Contact and list management.
- Segmentation.
- Visual MJML email builder.
- Liquid templating.
- Email automations.
- A/B testing.
- Delivery-provider integrations.
- Webhooks.
- Message history.
- Open and click tracking.
- Built-in cookieless web analytics.
- Optional SMTP bridge.
- Notification center widget.
If Inbucket answers “did my app send this email?”, Notifuse answers “how do I manage all the emails my product sends?”
How Notifuse Fits A Homelab
For a homelab or small product stack, Notifuse can become the control plane for product email.
Examples:
- Send newsletters to subscribers.
- Manage product announcement campaigns.
- Build transactional templates for signups, invites, receipts, and notifications.
- Segment contacts by fields or behavior.
- Track campaign links and web conversions.
- Build automation journeys.
- Use a real provider only for final delivery.
It does not eliminate the need for a trustworthy email delivery provider. For production, you still want SES, Postmark, Mailgun, SendGrid, Mailjet, SparkPost, or a properly configured SMTP provider.
For testing, you can pair Notifuse with Inbucket:
Notifuse -> test SMTP -> Inbucket -> inspect captured email
That gives you a safe way to review templates before connecting real outbound mail.
Tech Overview
The backend is Go, with a clean architecture layout:
cmd/apifor the application entrypoint.configfor environment and setup configuration.internal/domainfor business models.internal/servicefor workflows.internal/repositoryfor PostgreSQL access.internal/httpfor handlers and middleware.internal/migrationsfor database migrations.
The admin console is a React and TypeScript app under console/, built with Vite, Ant Design, TanStack Router, TanStack Query, TipTap, Monaco, MJML tooling, ECharts, and Lingui.
There are also separate frontend packages for:
notification_center/web_analytics_sdk/
PostgreSQL is the required database. The upstream Compose file uses PostgreSQL 17.
Self-Hosting Notifuse with Docker
The documented Compose path is straightforward on a clean Docker host:
git clone https://github.com/Notifuse/notifuse.git
cd notifuse
docker compose up -d
That Compose file builds the app locally and publishes:
8081 -> 8080 web app
587 -> 587 optional SMTP bridge
5433 -> 5432 PostgreSQL
On my server, I did not run that Compose file as-is.
Reason:
- This homelab already has many running containers.
- Port
587is a mail-submission port and should not be casually claimed. - PostgreSQL did not need to be published to the host for a smoke test.
- This Docker daemon previously failed when creating new user-defined networks because its address pools were exhausted.
So I used a safer field-test layout.
Safe Field Test Without Touching Existing Containers
This test used:
notifuse-safe-postgresfor PostgreSQL.notifuse-safe-apifor the Notifuse app.- Docker’s existing
bridgenetwork. - Web host port
8326. - Optional SMTP bridge host port
2587. - No published PostgreSQL port.
- No user-defined Docker network.
Start PostgreSQL:
docker run -d --name notifuse-safe-postgres \
--network bridge \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=notifuse-safe-postgres \
-e POSTGRES_DB=postgres \
postgres:17-alpine
Wait until it is ready:
docker exec notifuse-safe-postgres pg_isready -U postgres
Get its bridge IP:
PG_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' notifuse-safe-postgres)
Start Notifuse:
SECRET_KEY=$(openssl rand -base64 48)
docker run -d --name notifuse-safe-api \
--network bridge \
-p 8326:8080 \
-p 2587:587 \
-e SERVER_PORT=8080 \
-e SERVER_HOST=0.0.0.0 \
-e ENVIRONMENT=production \
-e DB_HOST="$PG_IP" \
-e DB_PORT=5432 \
-e DB_USER=postgres \
-e DB_PASSWORD=notifuse-safe-postgres \
-e DB_PREFIX=notifuse \
-e DB_NAME=notifuse_system \
-e DB_SSLMODE=disable \
-e SECRET_KEY="$SECRET_KEY" \
-e API_ENDPOINT=http://localhost:8326 \
-e WEBHOOK_ENDPOINT=http://localhost:8326 \
-e TELEMETRY=false \
-e CHECK_FOR_UPDATES=false \
notifuse/notifuse:latest
Then open:
http://localhost:8326/console
From another device on your LAN:
http://SERVER_IP:8326/console
What Worked In The Field Test
The container became healthy:
curl http://localhost:8326/healthz
Response:
{"status":"ok"}
The root path redirected to the console:
/ -> /console
The console served HTML successfully at:
http://localhost:8326/console
Setup status returned:
{
"is_installed": false,
"smtp_configured": false,
"api_endpoint_configured": true,
"root_email_configured": false,
"smtp_bridge_configured": false,
"oidc_configured": false
}
That is the expected first-run state. The app is alive, PostgreSQL is connected, and the setup wizard is waiting for the root email and SMTP configuration.
The logs also confirmed:
- App version
38.0. - PostgreSQL connection succeeded.
- First-run database version initialized.
- Setup wizard required.
- GeoIP database loaded.
- Server started on
0.0.0.0:8080.
Completing Setup
The setup wizard asks for things such as:
- Root administrator email.
- API endpoint.
- SMTP settings.
- Telemetry/update preferences.
- Optional SMTP bridge settings.
- Optional OIDC settings.
For a local test, you can use:
API endpoint: http://localhost:8326
For production, use your real HTTPS endpoint:
https://mail.yourdomain.com
For outbound SMTP, either connect a real delivery provider or use a test sink like Inbucket while you are learning.
Important Production Notes
Do not reuse the default SECRET_KEY from example files.
Generate one:
openssl rand -base64 48
Keep PostgreSQL private:
Do publish: Notifuse web/API port behind HTTPS
Do not publish: PostgreSQL directly to the internet
Use a reverse proxy such as Caddy, Traefik, Nginx, or Cloudflare Tunnel for the web console.
Be careful with the SMTP bridge. Publishing port 587 means you are exposing an SMTP listener. Only enable it when you understand the authentication, TLS, and routing model.
Notifuse vs Inbucket
These two tools are complementary.
| Tool | Use Case |
|---|---|
| Inbucket | Catch and inspect fake/test emails |
| Notifuse | Manage real campaigns, transactional email, templates, contacts, automations, and analytics |
During development:
Notifuse -> Inbucket
In production:
Notifuse -> SES/Postmark/Mailgun/SendGrid/Mailjet/SparkPost/SMTP
Things I Did Not Do
For this first pass, I stopped at the safe setup-wizard boundary.
I did not:
- Complete the setup wizard.
- Create a root admin account.
- Configure a real SMTP provider.
- Send real email.
- Expose Notifuse publicly.
- Publish PostgreSQL to the host.
- Run the upstream Compose file as-is.
- Create a new Docker network.
That is intentional. It confirms the app runs without risking your current homelab containers or accidentally sending mail.
Conclusion
Notifuse is a serious self-hosted email platform, not a tiny utility.
It brings together campaign sending, transactional email, contacts, automations, templates, provider integrations, and analytics in one Go/React/PostgreSQL application. The safe Docker test worked on this server with high ports and without touching existing containers.
If you want to evaluate it further, the next practical step is to complete the setup wizard using a test SMTP target, ideally Inbucket, before connecting a real delivery provider.
Notifuse is open source under the AGPL-3.0 license.
FAQ
What is the practical use case for Notifuse?
Notifuse is for managing product email.
Use it when you want newsletters, broadcasts, transactional templates, contacts, segments, automations, provider integrations, and analytics in one self-hosted platform.
It is not just an SMTP inbox or a testing sink.
Where do I access the UI after the safe test?
With the tested port mapping:
http://localhost:8326/console
From another LAN device:
http://SERVER_IP:8326/console
The root path redirects to /console.
Does Notifuse send email by itself?
Notifuse manages email sending, but for real delivery you still configure an outbound provider such as Amazon SES, Mailgun, Postmark, Mailjet, SparkPost, SendGrid, or SMTP.
For local testing, point outbound SMTP to Inbucket so messages are captured instead of delivered to real inboxes.
Can I test registration emails, newsletters, and cold email with Notifuse and Inbucket?
Yes, for testing.
With Notifuse and Inbucket together, the safe test flow is:
Your app or Notifuse -> SMTP -> Inbucket -> inspect captured email
This is useful for:
- User registration emails.
- Magic login links.
- Password reset emails.
- Invite emails.
- Transactional notifications.
- Product announcements.
- Newsletter drafts.
- Campaign templates.
- Automation flows.
- HTML rendering checks.
- Links, UTM parameters, and unsubscribe-link checks.
- Segments and contact-list tests.
The important distinction is testing versus production delivery:
Testing:
Notifuse -> Inbucket
No real email leaves your server.
Production:
Notifuse -> SES / Postmark / Mailgun / SendGrid / Mailjet / SparkPost / SMTP provider
Real email is delivered.
For newsletters, Notifuse is a good fit because it has contacts, lists, templates, broadcasts, automations, tracking, and analytics.
For cold email, be careful. Notifuse can send campaigns, but cold outreach brings deliverability, consent, unsubscribe, legal, anti-spam, and domain-reputation requirements. Do not use a casual homelab SMTP path for real cold email. Use a proper provider, configure SPF/DKIM/DMARC, include unsubscribe handling, warm domains carefully, and stay compliant with the rules that apply to your recipients.
Can I use Inbucket with Notifuse?
Yes. Inbucket is useful as a safe SMTP target while configuring Notifuse.
The development flow is:
Notifuse -> sends SMTP -> Inbucket -> inspect captured email
That lets you verify templates, subjects, links, and rendered content before using a real provider.
How do I configure Notifuse to send test email into Inbucket?
Use Inbucket as Notifuse’s outbound SMTP server during setup.
With the tested containers on this server:
Notifuse UI: http://192.168.1.2:8326/console
Inbucket UI: http://192.168.1.2:9325/
Inbucket SMTP: 192.168.1.2:2525
In the Notifuse setup wizard, use:
API Endpoint: http://192.168.1.2:8326
SMTP Host: 192.168.1.2
SMTP Port: 2525
Use TLS: off / unchecked
SMTP Username: empty
SMTP Password: empty
From Email: [email protected]
From Name: Notifuse Test
EHLO Hostname: notifuse.local
The Test connection button should return success. After Notifuse sends a test or setup email, open Inbucket and inspect the mailbox that matches the recipient’s local part. For example, mail sent to [email protected] appears in mailbox admin.
One important browser caveat: if you access Notifuse from another machine on the LAN, do not configure API_ENDPOINT as http://localhost:8326. In the browser, localhost means the machine running the browser, not the server. Use the server IP instead:
http://192.168.1.2:8326
If the setup page reports NetworkError when attempting to fetch resource, check config.js:
curl http://192.168.1.2:8326/config.js
It should contain:
window.API_ENDPOINT = "http://192.168.1.2:8326";
After changing the endpoint, hard-refresh the browser with Ctrl+F5 so the setup page does not use a cached config.js.
Why not run the upstream compose.yaml exactly?
The upstream Compose file is fine for a clean test machine, but this server already has many homelab containers and previously hit Docker address-pool exhaustion when creating new user-defined networks.
The upstream file also publishes host ports 8081, 587, and 5433. For this test, I avoided those defaults and used 8326 for web, 2587 for the SMTP bridge port, and no published PostgreSQL port.
What is the setup wizard state after first boot?
The tested instance returned:
{
"is_installed": false,
"smtp_configured": false,
"api_endpoint_configured": true,
"root_email_configured": false,
"smtp_bridge_configured": false,
"oidc_configured": false
}
That means the app is running, but setup has not been completed yet.
What should I configure before production?
At minimum:
- A strong
SECRET_KEY. - HTTPS public API endpoint.
- Private PostgreSQL access.
- A real outbound email provider.
- Backups for PostgreSQL.
- Reverse proxy and access controls.
- Clear decision on telemetry and update checks.
- SMTP bridge disabled unless you explicitly need it.
Also review AGPL-3.0 license obligations before modifying and offering the service over a network.
Comments