# smtprelay, a small Go relay that hides your mail credentials from every device

> smtprelay is a Go SMTP relay that accepts mail on one port and forwards it to a smarthost, so cron jobs, printers and services never hold the credentials. It suits internal networks and it is not a mail server.

**decke/smtprelay** — Simple Golang SMTP relay/proxy server

- Repository: https://github.com/decke/smtprelay
- Stars: 638 · Forks: 133
- Language: Go
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/decke-smtprelay

## The problem is credentials, not mail routing

Most people who send mail from a server never think about SMTP at all until something like this happens: a network printer, a monitoring agent or a small service starts sending mail, and now each of those devices holds the credentials for the outbound account. smtprelay exists to break that pattern. The README states the use case plainly: automatically generated mails from cron via msmtp, sSMTP or dma, plus mails from various services and network printers, sent through a remote SMTP server without giving away mail credentials to each device that produces mail.

That reframing matters. smtprelay is not trying to be a mail server with a submission queue, virtual domains and per-user policy. It is a front door that terminates a client connection, checks it, and then opens its own authenticated session to a smarthost. Anything that can speak SMTP can be pointed at it instead of at the real account.

The audience is therefore an internal network, a homelab, or a small team with devices that must send but must not know. The README is blunt about the alternative being rejected: Postfix, Exim, Sendmail and OpenSMTPD are described as full blown kitchensinks that are complex, fragile and hard to configure. smtprelay answers that complaint with a small codebase rather than with a feature list.

## Everything goes to one smarthost, with no per-recipient routing

The mechanism is a straight proxy. smtprelay accepts mail over SMTP and forwards it directly to another SMTP server, and the README lists the feature as forwarding all mail to a smarthost, any SMTP server. The repository layout matches that description: remotes.go and smtp.go carry the SMTP side of the work, while main.go and config.go handle startup and configuration.

The consequence is a boundary worth stating plainly. There is no per-domain routing, no local delivery agent, and no mailbox store. Every message that arrives is handed to the same upstream server. That is what makes the tool small and it is also what disqualifies it from being a mail server. If a message is undeliverable, what happens to it is not described in the README, and the repository has no spool or queue directory among its top level entries.

The same section of the README lists the checks the relay performs: sender, receiver and client IP. Those are the filter points. The README does not document a rules file or expression syntax for them, so treat that list as the shape of the feature rather than as a full specification, and read config.go if you need the exact keys.

## Client authentication is file based, and encryption can be required

Authentication support is listed as file based, with the mechanisms LOGIN and PLAIN. The auth.go file in the repository is where that lives. A file of accounts is the simplest scheme that covers the described use case, where a handful of devices share one outbound identity.

Two details sharpen the picture. The feature list includes an option to enforce encryption for authentication, which fits the TLS support described separately: you can insist that a client does not send its password over a cleartext connection. The other detail is what is not listed. There is no mention of an external identity provider, no OAuth or token flow, and no database backed account store. If you need per user accounts sourced from somewhere else, this project does not describe that path.

For a security sensitive deployment, the honest summary is that the account model is a flat file, and the README documents neither password hashing for that file nor the rotation procedure for it.

## Three ports and three encryption postures

The README lists support for SMTPS or TLS on 465, STARTTLS on 587, and unencrypted SMTP on 25. Listing all three tells you the tool can sit in front of clients with different expectations, which is the whole point when printers and old software are involved.

Two of those modes are worth distinguishing carefully. SMTPS on 465 is implicit TLS: the connection is encrypted from the first byte. STARTTLS on 587 starts in the clear and is upgraded by command. That difference decides whether a client that only speaks legacy plain SMTP can be served, and it decides whether credentials are exposed during the handshake if you allow plain traffic. The option to enforce encryption for authentication exists precisely because plain SMTP on port 25 is supported.

The dependency list includes golang.org/x/crypto, which is where the cryptographic primitives for TLS and credential handling come from. The README does not describe the cipher suite policy, certificate handling, or whether a self signed certificate is accepted for the client side of a STARTTLS upgrade, so verify that against the actual TLS options in config.go before pointing production clients at it.

## Configuration comes from an ini file, a .env file, or the environment

Configuration is described as simple, with three routes: an ini file, a .env file, or environment variables. The repository contains smtprelay.ini at the top level, so a sample configuration is shipped with the source, and config.go with its test file handle the parsing. The .env route matters for container deployments, where you would rather not bake a file into the image.

The dependency list explains the shape of the configuration surface. github.com/peterbourgon/ff/v3 is present, which is a flag and environment parsing library, so command line flags and environment variables are part of the same configuration mechanism rather than a separate layer. The repository has no single config struct file beyond config.go, and no schema is documented in the README, so the authoritative list of keys is smtprelay.ini itself.

That is the practical instruction for a first run: open smtprelay.ini, set the listen address, the remote smarthost host and port, the credentials for that smarthost, and the account file path, then check the same keys against the environment variable names the binary accepts.

## Aliases can be reloaded when the alias file changes

The alias support deserves separate attention because of how it is described: dynamic reload when the alias file changes. That is not a cosmetic detail. A relay that needs a restart to pick up a new name mapping is a relay that breaks on the first config change in production.

The implementation is separate from the SMTP core, which is visible in the repository listing. aliases.go and its test sit apart from smtp.go, and the dependency list includes github.com/fsnotify/fsnotify, a filesystem notification library. That is the mechanism: the process watches the alias file and reloads it when it changes, rather than reading it once at startup.

The interesting question, which the README leaves open, is what happens to a message that arrives while a reload is in progress. The README also does not say what syntax an alias file uses, or whether aliasing applies to the sender, the receiver or both. Since this feature exists to let a device post mail on behalf of a different identity, read aliases.go directly before you design a sender policy around it.

## The dependency list is short, and logging goes through zerolog and timberjack

The build is a modern Go module. The module path is github.com/decke/smtprelay and the language directive names the Go version the project targets.

```go
module github.com/decke/smtprelay
go 1.26.0
```

Direct dependencies are few: a logger, a file watcher, a UUID library, the flag parser, a testing library, an SMTP server library from chrj/smtpd, and the crypto module. The presence of github.com/chrj/smtpd suggests the SMTP server side is built on that package rather than hand rolled, which is a sensible division of labour for a project whose stated goal is to stay small.

Logging is handled by rs/zerolog, and github.com/DeRuina/timberjack sits next to it, which points to structured logging written to rotating files. The repository has logger.go for that job. No log format, log level flag or log file path is documented in the README, so treat the logging configuration as something to read from logger.go and the ini file rather than assume.

The overall dependency profile is one of the better arguments for the project: a small tree, well known packages, and no database or message queue in the path.

## MIT licensed, tagged releases, and a busy commit line

smtprelay is released under the MIT licence, with the text in the LICENSE file at the top level. MIT is permissive, short, and carries no source disclosure obligation for users who modify the code internally, which keeps the licensing question short for most internal deployments. There is also a SECURITY.md in the repository, so there is a stated place to report a vulnerability, though the README does not describe a disclosure process.

On maintenance, the repository is not archived and the last push was on 2026-10-19. Three releases appear in the recent history: v1.14.0 on 2026-06-24, v1.13.3 on 2026-05-03 and v1.13.2 on 2026-04-05. The gap between the newest tag and the last push means commits are landing on master between releases, so a deployment pinned to v1.14.0 will not match the current tree.

For upgrades, the surface is small. The repository is a flat list of Go files at the top level, main.go, smtp.go, auth.go, config.go, aliases.go, remotes.go and logger.go, each with a matching test file, which is a sign the project is organised by responsibility rather than by layer. Before bumping a version, diff smtprelay.ini between tags, since configuration keys can change in ways that are not obvious from a version number alone.

## Conclusion

Adopt smtprelay when several devices on a trusted network need to send mail and you would rather they never see the outbound credentials. Do not adopt it as an internet facing MTA, because the README describes forwarding everything to a smarthost and says nothing about queues, retries or delivery reports. Before you deploy, read smtprelay.ini for the exact configuration keys, confirm that clients can reach the port you bind and that the relay can reach the smarthost, and decide whether the relay address checks and alias reload behaviour in aliases.go match the way your printers are named.

## FAQ

### What does smtprelay do with the mail it receives?

It accepts mail via SMTP and forwards it directly to another SMTP server. The README lists the feature as forwarding all mail to a smarthost, any SMTP server, so there is no per recipient routing or local delivery.

### How is smtprelay configured?

Configuration can come from an ini file, a .env file, or environment variables. The repository ships a sample smtprelay.ini at the top level, and the flag library peterbourgon/ff/v3 in the dependency list means command line flags work through the same mechanism.

### Does smtprelay support encrypted connections?

Yes. The README lists support for SMTPS or TLS on port 465, STARTTLS on port 587, and unencrypted SMTP on port 25, and there is a separate option to enforce encryption for authentication.

### How does smtprelay authenticate sending clients?

Authentication is file based and uses the LOGIN and PLAIN mechanisms, implemented in auth.go. The README does not describe any external identity provider or account store beyond that file.

### Can smtprelay change its aliases without a restart?

Yes. The README lists alias support with dynamic reload when the alias file changes, and the dependency list includes the filesystem watcher fsnotify, with the logic in aliases.go.

## Sources

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

---

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