NAME

Configd::Language::postfix - main.cf and master.cf, which postfix has never had a conf.d for.

VERSION

version 0.001

SYNOPSIS

use Configd();

Configd->adopt('postfix');    # main.cf and master.cf become generated
Configd->build('postfix');    # which is what the systemd drop-in then runs

From a shell, which is how it is actually used:

configd adopt postfix
printf 'mydestination = example.com\n' > /etc/postfix/main.cf.d/50-example.cf
systemctl restart postfix

DESCRIPTION

Postfix has postconf -e and nothing else. It sets a parameter by rewriting main.cf, which is fine for a person at a terminal and wrong for anything automated: two things configuring the same server cannot both set mydestination, because the second one to run replaces what the first wrote rather than adding to it. Hosting two domains on one mail server is enough to hit it, and the failure is quiet -- mail for the first domain simply stops being local.

So main.cf and master.cf become generated files with main.cf.d and master.cf.d beside them, and each domain drops in a fragment naming itself. The parameters that are lists are merged as lists; see "ACCUMULATING PARAMETERS".

What the fragments look like

Exactly like the file they add to, because that is the point -- anything you would have written in main.cf is a fragment:

# /etc/postfix/main.cf.d/50-example.com.cf
mydestination = example.com
virtual_mailbox_domains = example.com
virtual_mailbox_maps = hash:/etc/postfix/virtual/maps

NAME

Configd::Language::postfix - main.cf and master.cf, which postfix has never had a conf.d for.

ACCUMULATING PARAMETERS

The parameters postfix documents as comma-or-space separated lists, where two fragments each naming a domain, a map or a milter both meant it: mydestination, mynetworks, relay_domains, the virtual_* family, the *_maps and *_checks families, smtpd_milters and inet_interfaces among them. Anything else is a value, and a later fragment replaces it.

The *_restrictions parameters are deliberately not accumulated even though they are lists. They are ordered, the order is what they mean, and joining two of them end to end gives something that parses and that neither fragment asked for -- a permit_ landing ahead of a check that was supposed to run first is an open relay. Two fragments disagreeing about a restriction list is something a person should look at.

METHODS

files()

main.cf and master.cf.

units()

postfix@.service, the templated unit.

services()

postfix.service, which is what can actually be restarted.

accumulates($key)

True for the list parameters above. Never true of a master.cf entry, which is a row rather than a list: two fragments configuring one service disagree about it, and the later one wins. The base class's comma is therefore the only separator this language ever needs.

parse($text)

Read a fragment of either file.

Which one is worked out from the text rather than from a filename, because a main.cf line and a master.cf line cannot be mistaken for each other: the first has an = and the second is a row of columns.

emit($directives)

Write the file back. Which file, again, from what is in it.

SEE ALSO

Please see those modules/websites for more information related to this module.

BUGS

Please report any bugs or feature requests on the bugtracker website https://github.com/teodesian/perl-configd/issues

When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature.

AUTHORS

Current Maintainers:

  • George S. Baugh <george@troglodyne.net>

COPYRIGHT AND LICENSE

Copyright (c) 2026 Troglodyne LLC

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.