#74 CentOS 7: postfix, dovecot, Roundcube, amavisd-new, spamassassin, clamav

In one of my previous posts I’ve described how to run a postfix and dovecot on CentOS 6. This time, we’ll go a step further and after the installation of postfix and dovecot, we’ll talk about mail filtering, spam and anti-virus protection.

Pre install

The post described below uses:

  • CentOS 7.1.1503 (fresh install)
  • Kernel 3.10.0-229.4.2.e17.x86_64
  • postfix 2.10.1
  • dovecot 2.2.10
  • amavisd-new 2.10.1
  • spamassasin 3.4.0
  • clamav 0.98.7
  • pigeonhole 0.4.3

and will allow you to use virtual e-mail domains and users.


CentOS 7 comes with postfix preinstalled, so there is no need to install it first. Verify that postfix is installed and enabled to run on boot.

systemctl status postfix | grep enabled
   Loaded: loaded (/usr/lib/systemd/system/postfix.service; enabled)

NOTE: Highlighted numbers are what you type. The rest is the response from the server.

The configuration files for postfix are in /etc/postfix. There are two main files, and Make a copy of both these files.

cd /etc/postfix

On CentOS postfix also comes with a postfix username and postfix group that are used to run the daemon, but we’ll need a separate user and group. Technically we can use postfix user but it’s not recommended to use any UID that’s lower than 500. So, let’s create a new user and a group.

groupadd vpostfix && useradd vpostfix -g vpostfix -s /sbin/nologin -c "Virtual postfix user" -d /var/empty

Ignore the warning that the home directory exists, that’s fine.
Get the UID and GID because we will need these numbers for the configuration.

grep vpostfix /etc/passwd && grep vpostfix /etc/group
vpostfix:x:1001:1001:Virtual postfix user:/var/empty:/sbin/nologin

In my case the output was this, which means the UID is 1001 and GID is 1001.
Now, edit and change the following values:

myhostname =
mydomain =
myorigin = $mydomain
inet_interfaces = all
home_mailbox = Maildir/

Replace with whatever your FQDN of the server is. Also, note that by default inet_interfaces = localhost is enabled by default. You have to comment this line. We will also use Maildir format instead of mbox because it’s much better.
While editing, add these lines at the end of the file. Make sure you replace UID and GID. Don’t just blindly copy & paste.

# Virtual domain config
virtual_mailbox_domains = /etc/postfix/virtual_domains
virtual_mailbox_base = /var/mail/vhosts
virtual_mailbox_maps = hash:/etc/postfix/vmailbox
# Make sure you replace these UID:GID numbers
virtual_minimum_uid = 1001
virtual_uid_maps = static:1001
virtual_gid_maps = static:1001
virtual_alias_maps = hash:/etc/postfix/virtual

Now, create a new file called /etc/postfix/virtual_domains. This is the file where all of your domains will be listed. Of course, you’ll have to make sure that MX records of your domains point to the IP of the CentOS box.

cd /etc/postfix/
touch virtual_domains

The format looks like this.

#  Put each domain in a separate line.

Create the mail directory, sub-directories for the domains and assign the proper permissions. This is where the mail will be stored for all virtual domains.

mkdir /var/mail/vhosts
chgrp -R vpostfix /var/mail
cd /var/mail/vhosts
cd ..
chown -R vpostfix:vpostfix vhosts

Once you do that, postfix will create the “Maildir” directories automatically and assign the proper permissions once an e-mail hits these destinations. Finally, create a file /etc/postfix/vmailbox and add all of the users that will receive e-mails. Here is an example: 

NOTE: Make sure you end up each line with “/”, otherwise mail won’t be delivered.

Virtual user “” (mind that there is no CentOS login for this user, these are all virtual users) will have his email delivered under /var/mail/vhosts/ directory. You don’t have to create these sub-directories. Once everything is up and running, postfix will take care of creating the Maildir structure (cur, new, tmp).

If you want you can create a catch-all address, see the example above (catch-all). This line tells postfix to get all the emails for the non-existing users in that domain (, which means a lot of spam. This is definitely not a recommended practice.

But what if you have a valid CentOS user named bill? Where that email goes? In this case, nowhere. If we want this OS user to receive an email, we’ll have to treat him as a virtual user and add him to a virtual domain. It’s much easier to maintain one list of virtual users and hosts than deal with separate configuration files.

Maybe you’ve noticed that the file with the e-mail addresses (vmailbox) has a hash: prefix in the config file. This is to speed-up lookups. Postfix can use hash: (Berkeley-DB), mySQL or PostgreSQL databases to store the e-mail accounts. Check the postfix howto if you want to use mySQL or PostgreSQL. We’ll be dealing with Berkeley DB.

Create the virtual aliases file and create a local aliases file.

touch /etc/postfix/virtual
cd /etc
postalias aliases

Once we are done with editing these files, do the following to create the hashed files (extension .db).

NOTE: You should execute these lines anytime you make a change to these files (virtual_domains and vmailbox).

postmap /etc/postfix/virtual
postmap /etc/postfix/vmailbox

At this point you can restart postfix so all changes that we made will take effect.:

systemctl restart postfix

Check the log file with:

tail /var/log/maillog

You should see that the daemon is started.

Check if postfix runs and listens on port 25.

ps -eaf | grep postfix
root      1627     1  0 Jun01 ?        00:00:00 /usr/libexec/postfix/master -w
postfix   1647  1627  0 Jun01 ?        00:00:00 qmgr -l -t unix -u
postfix  19407  1627  0 10:53 ?        00:00:00 pickup -l -t unix -u
postfix  19853  1627  0 12:05 ?        00:00:00 smtpd -n smtp -t inet -u -s 2
postfix  19854  1627  0 12:05 ?        00:00:00 proxymap -t unix -u
root     19862 19748  0 12:06 pts/0    00:00:00 grep --color=auto postfix
ss -l | grep smtp
u_str  LISTEN     0      100       private/smtp 14560                 * 0
tcp    LISTEN     0      100                  *:*
tcp    LISTEN     0      100                ::1:smtp                 :::*

NOTE: You can stop and restart postfix with systemctl start postfix and systemctl stop postfix or reload the configuration files with systemctl reload postfix.

From another domain (e.g. your hotmail or gmail account) send an e-mail to or whatever your domain is and watch the log file.

tail -f /var/log/maillog

You should see something like this.


If you don’t see anything in the log and you verified that postfix is listening on port 25 from the server itself, most likely it’s a firewall issue. Open the SMTP port.

firewall-cmd --add-service=smtp --permanent
firewall-cmd --reload

If you check /var/mail/vhosts/domain-one/joe/new directory you’ll see a file with some gibberish name. This is your e-mail that you just sent to joe. But, how will this virtual user retrieve this e-mail? There is a login (the e-mail address), but what’s the password?


In order to retrieve the e-mails, we’ll configure dovecot. Dovecot is an open-source POP and IMAP client.
As of version 2.0, there are multiple configuration files for dovecot. The main file is /etc/dovecot/dovecot.conf, but you’ll see a lot of include directives there that point to /etc/dovecot/conf.d directory where we have multiple configuration files. CentOS doesn’t come up with dovecot installed, so we have to install it first.

yum install dovecot

Make a copy of /etc/dovecot/dovecot.conf and remove the comment from this line.

protocols = imap pop3 lmtp

Then, go to conf.d directory and change the following lines in the following files.


disable_plaintext_auth = no
#!include auth-system.conf.ext
!include auth-passwdfile.conf.ext


log_path = /var/log/dovecot.log
auth_verbose = no
auth_debug = no
verbose_ssl = no


mail_home = /var/mail/vhosts/%d/%n
mail_location = maildir:~
mail_uid = 1001    # These are the GID and UID numbers for postfix
mail_gid = 1001    # Don't just put random numbers here. Check above.
mail_privileged_group = vpostfix


unix_listener auth-userdb {
  mode = 0600
  user = vpostfix
  group =  vpostfix
# Postfix smtp-auth
unix_listener /var/spool/postfix/private/auth {
  mode = 0666
  user = vpostfix
  group = vpostfix


ssl = no
# ssl_cert = </etc/ssl/certs/dovecot.pem
# ssl_key = </etc/ssl/private/dovecot.pem

If you look at 10-auth.conf, we commented the line #!include auth-system.conf.ext and uncommented the !include auth-passwdfile.conf.ext. Take a look at this file (auth-passwdfile.conf.ext) and you’ll see:

passdb {
  driver = passwd-file
  args = scheme=CRYPT username_format=%u /etc/dovecot/users
userdb {
  driver = passwd-file
  args = username_format=%u /etc/dovecot/users

This tells us that our username/password database will be in the file /etc/dovecot/users. To generate a password with SHA512-CRYPT password scheme do:

doveadm pw -s SHA512-CRYPT

You’ll be prompted to enter a password twice and the output will be similar to this.


If you want to use a different password scheme, take a look at this link.
Now, create or open /etc/dovecot/users and copy and paste the password after the username. In my case, I have with some password that I just generated. So the line will be like this.


Don’t forget to add 4 colons after the password “::::”. Even if you use the same password for the users, they’ll be encrypted differently.

The problem with this scenario is that the end users won’t have the ability to change their passwords. So, you’ll have to provide them with the password and they won’t be able to reset them. But, there are plenty of perl scripts that can take care of this or you can write your own.

Start dovecot, enable it to start on boot and check for any errors. At this point, we should have dovecot running and listening for pop and imap connections.

systemctl start dovecot
systemctl enable dovecot
tail /var/log/dovecot.log
ss -l | grep pop3
ss -l | grep imap

Now, let’s check our e-mail. You can do that from the server using the telnet command.

NOTE: Highlighted numbers are what you type. The rest is the response from the server.

telnet localhost 110
Connected to localhost.
Escape character is '^]'.
+OK Dovecot ready.
pass topsecret
+OK Logged in.
+OK 2 2037
+OK 2 messages:
1 1027
2 1010
+OK Logging out.
Connection closed by foreign host.

In the above example, I am testing POP3. For IMAP, do the following.

telnet localhost 143
Connected to localhost.
Escape character is '^]'.
? login topsecret
? list "" "*"
* LIST (\HasNoChildren) "." INBOX
? OK List completed.
? logout
* BYE Logging out
? OK Logout completed.
Connection closed by foreign host.

If you want you can test retrieving these emails from a mail client such as Outlook, Opera Mail or any MUA of your preference. At this point the server can receive e-mails from others and you can retrieve those e-mails from outside using POP and IMAP. What we need to do now is to be able to reply to those e-mails from outside (using MUA of your choice). Nowadays port 25 is blocked at some major providers (Verizon, Comcast for example), so we’ll use SASL in Postfix and we’ll use Dovecot to authenticate the users using the same username/password combination. In addition, we’ll use certificates, so instead of POP3 and IMAP, we’ll use their secure equivalents, POP3s and IMAPs running on ports 995 and 993 respectively. At this point you can open ports 993 and 995 if you want and close 110 and 143 on the firewall. We won’t be using these ports (POP3 and IMAP).

postfix and TLS

Edit /etc/postfix/ and add the following lines at the end.

smtpd_use_tls = yes
smtpd_tls_security_level = may
smtpd_tls_auth_only = yes
smtpd_tls_key_file = /etc/postfix/myserver.key
smtpd_tls_cert_file = /etc/postfix/server.crt
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
smtpd_sasl_type = dovecot
broken_sasl_auth_clients = yes
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination
smtpd_relay_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination

NOTE: As of postfix 2.10 the last line is needed. See this link.
Then, edit /etc/postfix/ and remove the comments from the submission part.

submission inet n       -       n       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_reject_unlisted_recipient=no
#  -o smtpd_client_restrictions=$mua_client_restrictions
#  -o smtpd_helo_restrictions=$mua_helo_restrictions
#  -o smtpd_sender_restrictions=$mua_sender_restrictions
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING

Restart postfix after these changes.

systemctl restart postfix

For information of what these values mean, check the links at the end of this post. If you do

ss -l | grep submission
grep submission /etc/services

you’ll see that postfix is also listening on port 587. Allow this port on the firewall if you don’t have it enabled, but don’t close port 25. This port is used for server to server communication.

firewall-cmd --add-port=587/tcp
firewall-cmd --reload

If you do telnet localhost 587 and type EHLO you should see that postfix replies with STARTTLS.

telnet localhost 587
Trying ::1...
Connected to localhost.
Escape character is '^]'.
220 ESMTP Postfix
250-SIZE 10240000
250 DSN

Exit by pressing CTRL-] and then type quit.

dovecot and SSL

Edit 10-auth.conf and change:

disable_plaintext_auth = yes

Then, edit 10-ssl.conf and change:

ssl = yes
ssl_cert = </etc/postfix/server.crt
ssl_key = </etc/postfix/myserver.key

We’ll use self-signed certificates, but check for free certificates. Self-signed certificates are fake, so you’ll get a prompt to accept a fake certificate when you try to send/receive an email, but the goal is to show you how to use them, not to be a 100% compliant.

Unlike virtual Apache domains, you don’t need multiple certificates for each virtual domain.

cd /etc/postfix
openssl genrsa -out myserver.key 1024
openssl req -new -key myserver.key -out myserver.csr

You have to answer some questions for the certificate request.

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:NJ
Locality Name (eg, city) []:Lawrenceville
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Joe's Plumbing
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []
Email Address []

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Sign the certificate.

openssl x509 -req -days 3650 -in myserver.csr -signkey myserver.key -out server.crt

The certificate files should be under /etc/postfix now. Restart both postfix and dovecot.

systemctl restart postfix
systemctl restart dovecot

You can test SMTP SSL/TLS on submission port 587.

openssl s_client -starttls smtp -connect localhost:587

Then type ehlo , hit ENTER and then mail If these steps work, you should be OK. To test SASL with postfix and dovecot, type:

doveadm auth test -a /var/spool/postfix/private/auth secret
passdb: auth succeeded
extra fields:

If you want you can open the firewall for IMAPs and POP3s.

firewall-cmd --add-port=993/tcp
firewall-cmd --add-port=995/tcp
firewall-cmd --reload

At this point, you should be able to send e-mails from your favorite MUA, but you’ll have to make some changes in order to send and receive. For example, in Outlook, you should use these settings.


So, no more port 110 and 143. Instead use 995 for POP3s, 587 for SMPT (SASL) and 993 for IMAPs. The username is your e-mail address and the password is the one that you generated with doveadm pw command.

Mind that if you use a web client like Roundcube and Roundcube is installed on the server where postfix and dovecot reside, you don’t have to open any port except 25.

Roundcube IMAP webmail client

In order to send/receive e-mails using a web client, you can use Roundcube. Please follow these guides to install it.

CentOS 7: Install LAMP (Linux, Apache, MySQL, PHP) + WordPress
CentOS 7: Install RoundCube Web Mail Client

Amavisd, Spamassassin and clamav

This software trio is used to fight spam messages and e-mails with virus attachments. Amavisd is used as an interface between postfix as MTA (mail transfer agent) and the content checkers (spamassassin and clamav). Clamav and spamassassin will be installed automatically once you install amavisd-new.

yum install amavisd-new
yum install clamav-udpate

First, let’s configure amavisd. The configuration file is /etc/amavisd/amavisd.conf. Edit this file and make sure that these values are correct.

# @bypass_virus_checks_maps = (1);  # controls running of anti-virus code
# @bypass_spam_checks_maps  = (1);  # controls running of anti-spam code
# $bypass_decode_parts = 1;         # controls running of decoders&dearchivers
$daemon_user  = 'amavis';     # (no default;  customary: vscan or amavis), -u
$daemon_group = 'amavis';     # (no default;  customary: vscan or amavis), -g
$mydomain = '';   # a convenient default for other settings (change it)
$MYHOME = '/var/spool/amavisd';   # a convenient default for other settings, -H (remove the comment in front)
@local_domains_maps = ( [".$mydomain",""] );  # list of all local domains. If you have multiple domains, add the here.
$myhostname = '';  # must be a fully-qualified domain name! (remove the comment in front)

There are a lot of changes that you can configure, but these are the basic ones. See the official page for more information.
Finally, let’s make sure that amavisd and clamav know about each other. Go to line 383 and see if this is correct (383G in vi).


Clamav eats up a lot of memory. You won’t be able to run it on a server with less than 2GB RAM. If you feel comfortable, you can disable it. Don’t uncomment the lines below, remove the comment from # @bypass_virus_checks_maps = (1); # controls running of anti-virus code and

# ###
  \&ask_daemon, ["CONTSCAN {}\n", "/var/run/clamav/clamd.sock.sock"],
  qr/\bOK$/m, qr/\bFOUND$/m,
  qr/^.*?: (?!Infected Archive)(.*) FOUND$/m ],
# # NOTE: run clamd under the same user as amavisd - or run it under its own
# #   uid such as clamav, add user clamav to the amavis group, and then add
# #   AllowSupplementaryGroups to clamd.conf;
# # NOTE: match socket name (LocalSocket) in clamav.conf to the socket name in
# #   this entry; when running chrooted one may prefer a socket under $MYHOME.

Now, let’s tie everything together with postfix. Edit /etc/postfix/ and add these lines at the end.

# Amavisd
amavisfeed unix - - n - 2 lmtp
        -o lmtp_data_done_timeout=1200
        -o lmtp_send_xforward_command=yes inet n - n - - smtpd
        -o content_filter=
        -o smtpd_delay_reject=no
        -o smtpd_client_restrictions=permit_mynetworks,reject
        -o smtpd_helo_restrictions=
        -o smtpd_sender_restrictions=
        -o smtpd_recipient_restrictions=permit_mynetworks,reject
        -o smtpd_data_restrictions=reject_unauth_pipelining
        -o smtpd_end_of_data_restrictions=
        -o smtpd_restriction_classes=
        -o mynetworks=
        -o smtpd_error_sleep_time=0
        -o smtpd_soft_error_limit=1001
        -o smtpd_hard_error_limit=1000
        -o smtpd_client_connection_count_limit=0
        -o smtpd_client_connection_rate_limit=0
        -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_milters,no_address_mappings
        -o local_header_rewrite_clients=
        -o smtpd_milters=
        -o local_recipient_maps=
        -o relay_recipient_maps=

Edit /usr/local/etc/postfix/ and add these lines at the end.

# Amavisd
content_filter = amavisfeed:[]:10024

Before we start these three daemons, let’s make some changes. First, edit /etc/freshclam.conf and remove or comment the Example line.

# Comment or remove the line below.

Then, edit /etc/sysconfig/freshclam and remove all 4 lines at the bottom.

### !!!!! REMOVE ME !!!!!!
### REMOVE ME: By default, the freshclam update is disabled to avoid
### REMOVE ME: network access without prior activation

Once you do that, update the antivirus definitions by executing:


Then update spamassassins’s signatures.

sa-update -D

The “-D” option is to run in debug mode so you can see what’s going on. If there is an update available, spamassassin’s exit code is 0, if not the exit code is 1. In case there is an update, we’ll have to restart the spamassassin’s daemon.
Check the cron folder and make sure you see both calamav-update and sa-update files.

cd /etc/cron.d
ls -l clamav-update sa-update

Now, we can start everything.

systemctl start amavisd
systemctl enable amavisd
systemctl start spamassassin
systemctl enable spamassassin

Once you start amavisd, clamd starts as well.
Finally, let’s restart postfix.

systemctl restart postfix

Check the connection between amavisd and postfix.

telnet localhost 10024

Type ehlo localhost and check the ouput. In my case it looks like this.


Then do.

telnet localhost 10025

Again, type ehlo localhost and check the ouput. In my case it looks like this.
250-SIZE 10240000
250 DSN

And finally, some real tests. First, check the mail log file.

tail -f /var/log/maillog

Then, from another e-mail account, send a text (not HTML) e-mail with this in the body.


You should see something like this in the logs.


Leave the log file open and let’s send another test e-mail, same text format, but this time put this line in the body of the message.


You should see something like this in the log file.


Stress test

From another server with postfix installed, do:

time /usr/sbin/smtp-source -s 40 -l 10120 -m 5000 -c -f -t

On your server do:

tail -f /var/log/maillog

Make sure that the server where you run smtp-source is a legit server, otherwise your postfix will just reject all messages.
Watch how your log file gets bombarded with messages. You can also watch the queue real-time with:

postqueue -p

If you are satisfied with the results after 5-10 mins, empty the postfix queue with:

postsuper -d ALL
#94 Amazon AWS: Elastic File System (EFS) on FreeBSD/RHEL/CentOS/AWS Linux
#29 Multi-boot installation of Windows XP, Windows 2008 R2, Windows 7, FreeBSD, CentOS 6.2 and BackTrack 5.1
#95 CentOS: Setup Samba as an AD Domain Member
  • Arturs Smirnovs

    Grate article! Made my day and my emails running :)
    Thank you.

    P.S. For others who search articles how to install postfix and dovecot on internet: This works :) tried some other tutorials but all fail for some reason. but this works nicely. :)

  • Zoltán László

    I can repeat only the previous comment. This works. Not only works, it’s well commented. I tried a many other tuts but I used to found some differences and not working commands, logic errors, etc.

    Thanks for this article.

  • Yuris Sarek

    I followed this tutorial to setup a mail server on CentOS 7. I can send and receive emails internally (mydomain) but externally (other domains,gmail,…) I can only receive mails but I can’t send. When I try to send an email I get this error:
    Relay access denied (in reply to RCPT TO command)). Can somebody help to fix this.


  • Ian Brown

    A couple of things that might help others — This blog post is over a year old and some things have apparently changed.

    The post mentions pigeonhole at the top but then never discusses it later on. Needed?

    Don’t forget to re-comment out “protocols = imap pop3 lmtp” in dovecot.conf after enabling tls/ssl. It’s not needed and will put the file back to a pristine state — good for future issue-less rpm updates.

    Speaking of SSL/tls, a better choice for ssl certs are the free ones from lets encrypt ( Once configured it can be ran in a cronjob daily to always update the cert when it’s near expiration time too.

    I don’t recommend changing the logging settings for dovecot from the original “syslog” setting. Entries go into /var/log/maillog which is checked with logwatch if that rpm is installed. That file is also log-rotated while the one suggested here will grow indefinitely.

    If you install the rpm “clamav-server-systemd” you don’t have to touch the clamav socket settings in amavisd. Everything works out of the box.

    The only other thing missing is a way to retrieve emails sent to root — like errors in cron jobs, logwatch output, etc.

  • Rob Derby

    I’ve followed repeatedly your excellent description until dovecot with SSL, but don’t get the authentication verified when sending mail with telnet localhost 110 or 143.
    I always get
    pass topsecret
    -ERR [AUTH] Authentication failed.

    I compared your changes to the different configuration files several times and cannot find a difference.
    The output in dovecot.log is
    May 12 14:35:23 pop3-login: Info: Aborted login (auth failed, 1 attempts in 6 secs): user=, method=PLAIN, rip=, lip=, secured, session=

    If you could point me into the right direction, it would be very helpful. Thanks

    • Kliment Andreev

      You are right. I just built a test instance in AWS with CentOS 7.2.1511 and looks like something broke with certificates. I did the same exact steps up to “postfix and TLS” paragraph and I was able to receive e-mails. Once I made the changes in, things broke when I tried openssl s_client -starttls smtp -connect localhost:587. It just ends up the session with self signed certificate return code 18.

  • Evgeniy Demidchenko

    In my case the option

    mail_home = /var/mail/vhosts/%d/%n

    did not work, it was absent in conf file also. Fixed with editing option `mail_location`, and removed `mail_home`

    mail_location = maildir:/var/mail/vhosts/%d/%n