Porting my postfix gmail smtp to nixos

Hello, I am trying converting from debian to nixos.

I use gmail to send system notification. I usually set it up like this:

I set this options at /etc/postfix/main.cf

smtp_use_tls = yes
smtp_sasl_auth_enable = yes
smtp_sasl_security_options =
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
relayhost = [smtp.gmail.com]:587

Create a password file like this

# cat /etc/postfix/sasl_passwd
[smtp.gmail.com]:587    here_is_your_mail@gmail.com:pasw0rd_from_google_app_unique_passwords
#  postmap /etc/postfix/sasl_passwd

And add some aliases in /etc/aliases

as far I have this

  services.postfix = {
    enable    = true;
    submissionOptions.smtp_sasl_auth_enable = "yes";
    relayHost = "smtp.gmail.com";
    relayPort = 587;

How do I setup those configurations and how I use sops-nix to store the credentials? I already have a working sops storing rsa keys.


I’d bet

submissionOptions.smtp_sasl_password_maps = "hash:${config.sops.secrets.sasl_passwd.path}";

Sops docs also say you should make sure it start after sops, and configure user

{config, ...}: {
  systemd.services.postfix.after = [ "sops-nix.service" ];
  sops.secrets.sasl_passwd = {
    owner = config.services.postfix.user;
    key  = "sasl_passwd";


services.postfix (implementation)
sops-nix docs

Thanks I will try that!

Mostly I was wondering how the password file is hashed on configuration.
How to run postmap /etc/postfix/sasl_passwd

  • You encrypt the sasl_passwd with sops:
  • You configure sops-nix to use encrypted files
  • You configure your service to use ‘sops-nix’ path (ie config.sops.secrets.sasl_passwd.path)
  • When nix evaluates it, it change this path to ‘/run/sops/sasl_passwd’ (or something like that
  • When system starts, sops service decrypts that file at ‘/run/sops/sasl_passwd’
  • Then postfix, starts reading config that also points to that file


submissionOptions.smtp_sasl_password_maps = "hash:${config.sops.secrets.postfix_sasl_passwd.path}"; 

I don’t see any entry for the smtp_sasl_password_maps in the /etc/postfix/main.cf

Here is my current config if it helps:

I have sops working ok, but I thought that postfix accepts only a hashed password file (sasl_passwd.db) that is created from the clear text sasl_passwd file running an extra command (postman). I can not store the sasl_passwd.db in binary, in sops secrets.

Sorry If I am missing something. I am not familiar with postfix, I just needed, so my systems can send notification to my email.

Thanks for your time!

@thanasisn leaving a quick note because I came across this thread while trying to figure out the same thing!

The trick is to use services.postfix.mapFiles since those entries will automatically call postmap on them. Assuming that the sops secret is accessible by the postfix user (I use age-nix and I tweaked the secret to be readable by postfix) you’d need something like:

services.postfix = {
  enable = true;
  mapFiles.mycreds = config.sops.secrets.postfix_sasl_passwd.path;
  config = {
    # NB: mapFiles will put things in /var/lib/postfix/conf/<name>
    smtp_sasl_password_maps = "hash:/var/lib/postfix/conf/mycreds";

Hope this is useful to anyone else confused like I was!

1 Like

Doesn’t work, unfortunately. “postmap” is run as the “postfix” user, and /var/lib/postfix/conf is owned by root. With your suggested configuration I get:

Nov 28 13:05:38 myserver systemd[1]: Starting Setup for Postfix mail server...
Nov 28 13:05:38 myserver postfix-setup-start[472236]: postmap: fatal: open database /var/lib/postfix/conf/sasl_passwd.db: Permission denied
Nov 28 13:05:38 myserver postfix/postmap[472236]: fatal: open database /var/lib/postfix/conf/sasl_passwd.db: Permission denied
Nov 28 13:05:39 myserver systemd[1]: postfix-setup.service: Main process exited, code=exited, status=1/FAILURE

Which is strange, because then how the heck is the “mapFiles” attribute supposed to ever work…

EDIT: Just found an old bug report about it: services.postfix.{aliasFiles/mapFiles} fails if the file is not owned by root · Issue #67630 · NixOS/nixpkgs · GitHub

I try it, but I can not make it to work.
The clear text file exist but the /var/lib/postfix/conf/mycreds.db is not created, and all files are owned by root as @Hubro said.

Has anybody a complete postfix configuration of gmail?
This is the last thing I need to migrate to nixos.

I got:

ec 06 16:42:23 nixVM postfix-setup-start[10230]: postalias: warning: /etc/postfix/main.cf, line 32: overriding earlier entr>
Dec 06 16:42:23 nixVM postfix/postalias[10230]: warning: /etc/postfix/main.cf, line 32: overriding earlier entry: alias_maps>
Dec 06 16:42:23 nixVM postfix-setup-start[10232]: postmap: warning: /etc/postfix/main.cf, line 32: overriding earlier entry:>
Dec 06 16:42:23 nixVM postfix/postmap[10232]: warning: /etc/postfix/main.cf, line 32: overriding earlier entry: alias_maps=h>
Dec 06 16:42:23 nixVM postfix-setup-start[10232]: postmap: fatal: open database /var/lib/postfix/conf/mycreds.db: Permission>
Dec 06 16:42:23 nixVM postfix/postmap[10232]: fatal: open database /var/lib/postfix/conf/mycreds.db: Permission denied
Dec 06 16:42:24 nixVM systemd[1]: postfix-setup.service: Main process exited, code=exited, status=1/FAILUR

Config: https://github.com/thanasisn/nixos/blob/main/config.d/common_options.nix

Seems you just gotta let the secret be owned by root. It seems like “postmap” is run as whatever user owns the file, so if the file is owned by the postfix user then postmap won’t be allowed to create the hashed file.

Here’s my final working config:

  sops.secrets."postfix/sasl_passwd" = { mode = "0400"; };

  services.postfix = {
    enable = true;
    relayHost = "<redacted>";
    relayPort = 465;
    rootAlias = "<redacted>";
    mapFiles.sasl_passwd = "/run/secrets/postfix/sasl_passwd";
    config = {
      smtp_use_tls = "yes";
      smtp_sasl_auth_enable = "yes";
      smtp_sasl_security_options = "noanonymous";
      smtp_sasl_password_maps = "hash:/etc/postfix/sasl_passwd";
      smtp_tls_wrappermode = "yes";
      smtp_tls_security_level = "encrypt";
1 Like

Thank you very much!!
With some tries, I make it work!