Repository navigation
Relative aliases dont work #734
Copy link
Copy link
Open
Labels
bugSomething isn't working.Something isn't working.
Description
Describe the bug
I wanted to create an alternative name for my account using aliases file, but relative names like cat: dog (as specified in docs) didn't work for me. Maddy didn't do any modifications to the address and returned smtp error "User does not exist". I only managed to make it work with full emails in aliases file: cat@example.com: dog@example.com.
Is it error in documentations? For now it says:
# Replace 'cat' with any domain to 'dog'.
# E.g. cat@example.net -> dog@example.net
cat: dog
but this rule seems to do nothing for me.
Steps to reproduce
Configuration file
Spoiler warning
## Maddy Mail Server - default configuration file (2022-06-18)
# Suitable for small-scale deployments. Uses its own format for local users DB,
# should be managed via maddy subcommands.
#
# See tutorials at https://maddy.email for guidance on typical
# configuration changes.
# ----------------------------------------------------------------------------
# Base variables
$(hostname) = mx.example.com
$(primary_domain) = exmaple.com
$(local_domains) = $(primary_domain)
debug on
tls file /etc/maddy/certs/fullchain.pem /etc/maddy/certs/privkey.pem
# ----------------------------------------------------------------------------
# Local storage & authentication
# pass_table provides local hashed passwords storage for authentication of
# users. It can be configured to use any "table" module, in default
# configuration a table in SQLite DB is used.
# Table can be replaced to use e.g. a file for passwords. Or pass_table module
# can be replaced altogether to use some external source of credentials (e.g.
# PAM, /etc/shadow file).
#
# If table module supports it (sql_table does) - credentials can be managed
# using 'maddy creds' command.
auth.pass_table local_authdb {
table sql_table {
driver sqlite3
dsn credentials.db
table_name passwords
}
}
# imapsql module stores all indexes and metadata necessary for IMAP using a
# relational database. It is used by IMAP endpoint for mailbox access and
# also by SMTP & Submission endpoints for delivery of local messages.
#
# IMAP accounts, mailboxes and all message metadata can be inspected using
# imap-* subcommands of maddy.
storage.imapsql local_mailboxes {
driver sqlite3
dsn imapsql.db
}
# ----------------------------------------------------------------------------
# SMTP endpoints + message routing
hostname $(hostname)
table.chain local_rewrites {
optional_step regexp "(.+)\+(.+)@(.+)" "$1@$3"
optional_step static {
entry postmaster postmaster@$(primary_domain)
}
optional_step file /etc/maddy/aliases
}
msgpipeline local_routing {
destination postmaster $(local_domains) {
modify {
replace_rcpt &local_rewrites
}
deliver_to &local_mailboxes
}
default_destination {
reject 550 5.1.1 "User doesn't exist"
}
}
smtp tcp://0.0.0.0:25 {
limits {
# Up to 20 msgs/sec across max. 10 SMTP connections.
all rate 20 1s
all concurrency 10
}
dmarc yes
check {
require_mx_record
dkim
spf
}
source $(local_domains) {
reject 501 5.1.8 "Use Submission for outgoing SMTP"
}
default_source {
destination postmaster $(local_domains) {
deliver_to &local_routing
}
default_destination {
reject 550 5.1.1 "User doesn't exist"
}
}
}
submission tls://0.0.0.0:465 {
limits {
# Up to 50 msgs/sec across any amount of SMTP connections.
all rate 50 1s
}
auth &local_authdb
source $(local_domains) {
check {
authorize_sender {
prepare_email &local_rewrites
user_to_email identity
}
}
destination postmaster $(local_domains) {
deliver_to &local_routing
}
default_destination {
modify {
dkim $(primary_domain) $(local_domains) default
}
deliver_to &remote_queue
}
}
default_source {
reject 501 5.1.8 "Non-local sender domain"
}
}
target.remote outbound_delivery {
limits {
# Up to 20 msgs/sec across max. 10 SMTP connections
# for each recipient domain.
destination rate 20 1s
destination concurrency 10
}
mx_auth {
dane
mtasts {
cache fs
fs_dir mtasts_cache/
}
local_policy {
min_tls_level encrypted
min_mx_level none
}
}
}
target.queue remote_queue {
target &outbound_delivery
autogenerated_msg_domain $(primary_domain)
bounce {
destination postmaster $(local_domains) {
deliver_to &local_routing
}
default_destination {
reject 550 5.0.0 "Refusing to send DSNs to non-local addresses"
}
}
}
# ----------------------------------------------------------------------------
# IMAP endpoints
imap tls://0.0.0.0:993 {
auth &local_authdb
storage &local_mailboxes
}
Environment information
- maddy version: 0.7.1 linux/amd64 go1.23.3
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething isn't working.Something isn't working.
Activity
fix(aliases): Correctly handle relative aliases in table.file module (f…
foxcpp commentedon Oct 3, 2026
Interesting, unintended interaction:
optional_stepin table.chain preserves original value as a result. Ifreplace_addrsees any result for exact match, it will not fallback to relative match.foxcpp commentedon Oct 3, 2026
Workaround: Move aliases into separate modify step rather than a part of table.chain.
mvanhorn commentedon Oct 20, 2026
Exact-address lookup is treated as final as soon as it returns any value, so the local-part lookup is skipped. An optional chain step echoes its lookup key when it has nothing to substitute, and that echo is a single value equal to the normalized address.
When the exact lookup returns one value equal to the normalized address and that address has a domain, the result is cleared and the local-part lookup still runs. Any other exact result is kept and applied once.
#871