Safely run a command while Postfix locks a mailbox
You will run a command while postlock holds an exclusive lock on a UNIX-style mailbox file. This is useful for a mailbox reader, maintenance script or other tool that must not operate at the same time as Postfix local delivery. Allow about ten minutes for a first test. You need the Postfix package, a mailbox file you can read and write, and a command that is safe to run against it.
The route
Jump straight to the step you need, or tick off Done means at the end.
This guide describes the installed Postfix 3.8.6 command on this machine. The local manual page is the authority for the examples. Run the normal examples as the mailbox owner or another account with read/write permission; do not use sudo unless your account genuinely cannot access the mailbox and you have checked the consequences.
1. Confirm the installed command
Check which executable will run and record the package version before building a script around it:
$ command -v postlock
/usr/sbin/postlock
$ postconf -h mail_version
3.8.6
$ postlock
postlock: fatal: usage: postlock [-c config_dir] [-l lock_style] [-v] folder command...
The usage line confirms the important boundary: provide the mailbox file first, followed by the command and its arguments. The command is executed directly. postlock does not pass the remaining words to a shell for interpretation.
Checkpoint: if command -v finds nothing, stop and install or repair Postfix through your normal package-management process. If the version differs, recheck the local postlock(1) page before copying these details into automation.
2. Choose a mailbox and a harmless child command
Use an existing mailbox that the current account can read and write. A typical path might be /var/mail/alice, but do not guess the path or account on your host:
$ MAILBOX='/path/to/mailbox'
$ test -f "$MAILBOX" && test -r "$MAILBOX" && test -w "$MAILBOX" && echo 'mailbox is a writable regular file'
mailbox is a writable regular file
Replace the placeholder with the real path. This check does not change the mailbox. A missing result means you should fix the path or permissions first, not add sudo automatically. Running the child as root can create files or changes that the normal mailbox owner cannot later manage.
For a first smoke test, use a child that only appends a marker. This does change the mailbox, so make the marker deliberate and use a disposable test mailbox if possible. Do not run it against a production mailbox merely to prove that the command starts.
3. Run the command inside the lock
Pass the mailbox as the first argument to postlock. The example launches /usr/bin/sh explicitly so that the shell is the child command, not something postlock inserts behind your back:
$ postlock "$MAILBOX" /usr/bin/sh -c 'printf "postlock smoke test\n" >> "$1"' sh "$MAILBOX"
$ printf 'postlock status: %s\n' "$?"
postlock status: 0
The first sh after -c becomes the shell's $0; the mailbox path after it becomes $1. This slightly fussy arrangement keeps the placeholder path separate from the shell program text. Quote the path so spaces or shell metacharacters in it cannot change the command line.
Inspect only the expected marker after the test:
$ tail -n 3 "$MAILBOX"
postlock smoke test
The lock applies while the child runs. It is not a persistent permission change and it does not turn an unsafe mailbox reader into a safe one. Other programs must also co-operate with the same locking convention for this to prevent conflicts.
Checkpoint: a status of zero means the child completed successfully and postlock returned that status. It does not prove that every other mailbox-writing program respected the lock, or that a remote filesystem provides reliable exclusion.
4. Keep the command and its arguments unambiguous
A direct command is usually the safest form. For a program with no shell syntax, keep the executable and each argument as separate words:
$ postlock "$MAILBOX" /usr/local/bin/mailbox-tool --check "$MAILBOX"
This assumes that /usr/local/bin/mailbox-tool exists and accepts those arguments; substitute your actual program and consult its own manual page. Do not write a single string such as "/usr/local/bin/mailbox-tool --check $MAILBOX" and expect postlock to split or expand it. It will try to execute that whole string as a program name.
If shell features such as redirection, pipes or conditionals are genuinely required, invoke the shell explicitly as in the smoke test. Treat any path or data derived from another person as untrusted input. Never concatenate it into shell source without a separate, careful quoting design.
5. Understand configuration and diagnostics
By default, postlock reads Postfix configuration from the configured directory. On this installation, the default is /etc/postfix. Use postconf to inspect the effective locking-related values without editing configuration:
$ postconf -h config_directory
/etc/postfix
$ postconf -h mailbox_delivery_lock
fcntl, dotlock
The exact value can differ between hosts. The -l LOCK_STYLE option overrides the configured mailbox locking method for one invocation, but choose a value only after checking your local postlock(1) and Postfix configuration documentation. The -c CONFIG_DIR option selects another configuration directory, and -v enables verbose diagnostics. Repeating -v increases verbosity.
MAIL_CONFIG can also name the Postfix configuration directory, and MAIL_VERBOSE enables verbose logging. Be careful when these values come from a service environment: an unexpected configuration directory can change the locking behaviour. Check them with env or the service definition before debugging the mailbox itself.
6. Handle failures without guessing
If postlock cannot perform the requested operation, its result status is 75, also known as EX_TEMPFAIL. A status other than 75 may be the exit status from the child command, so capture it before running another diagnostic command:
$ postlock "$MAILBOX" /usr/bin/false
$ status=$?
$ printf 'status: %s\n' "$status"
status: 1
This example deliberately uses false, which exits with status 1. A status of 75 points towards a lock, permission, configuration or resource problem. Check the mailbox path, ownership, access mode and Postfix logs. Do not delete a lock file by hand merely because an attempt is inconvenient; stale-lock cleanup and the configured delay and attempt limits are part of Postfix's locking behaviour.
The manual warns that a lock acquired on a remote filesystem does not necessarily prevent access conflicts with processes on another machine. Keep cooperating mailbox processes on a filesystem and locking arrangement you understand. If a command was interrupted, inspect the mailbox and its logs before retrying a state-changing operation.
Done means
- You confirmed the installed Postfix version and the local
postlock(1)syntax. - You selected a real mailbox with read/write access and avoided unnecessary elevation.
- You passed the mailbox first and the executable plus arguments separately.
- You verified the child status and checked the expected mailbox result.
- You know that status 75 means
postlockcould not complete the operation, while other statuses can come from the child. - You have not assumed that remote filesystems or non-cooperating programs honour the lock.