Skip to content


Downloading and Installing Roundcube

First download the latest package. The latest version can be found by going to

sudo -s
cd /opt

Create our web directory:

mkdir /var/www/

Extract the tarball, move the newly created folder to web root and rename it as html at the same time.

tar xvf roundcubemail-1.4.9-complete.tar.gz
rm roundcubemail-1.4.9-complete.tar.gz
mv roundcubemail-1.4.9 /var/www/

Install any missing dependencies:

apt update && apt install php-net-ldap2 php-net-ldap3 php7.4-imagick php7.4-common php7.4-gd php7.4-imap php7.4-json php7.4-curl php7.4-zip php7.4-xml php7.4-mbstring php7.4-bz2 php7.4-intl php7.4-gmp

Install Composer, which is a dependency manager for PHP.

apt install composer

Change into the roundcube directory

cd /var/www/

Use composer to install all necessary 3rd party dependencies

composer install --no-dev

Create a dedicated user for the PHP pool we'll create later:

groupadd www-roundcube
useradd -g www-roundcube www-roundcube

Now add a temp folder and tighten permissions across the structure:

mkdir /var/www/
chown -R www-roundcube:www-data /var/www/
chown -R www-roundcube:www-roundcube /var/www/
chown -R www-roundcube:www-roundcube /var/www/
find /var/www/ -type d -exec chmod 750 {} \;
find /var/www/ -type d -exec chmod g+s {} \;
find /var/www/ -type f -name '*.php' -exec chmod 600 {} \;

Create a MariaDB Database

Log in to the MariaDB shell:

mysql -u root -p

Create a new database:


Create a user:

CREATE USER roundcubeuser@localhost IDENTIFIED BY 'password';

Grant permissions:

GRANT ALL PRIVILEGES ON roundcube.* TO roundcubeuser@localhost;

Flush privileges:


And exit:


Import the initial tables to the Roundcube database:

mysql roundcube < /var/www/

Create the Apache Virtual Host

Create a virtualhost file:

nano /etc/apache2/sites-available/

and enter the following:

  DocumentRoot /var/www/

  ErrorLog ${APACHE_LOG_DIR}/error.log
  CustomLog ${APACHE_LOG_DIR}/access.log combined

  <Directory />
    Options FollowSymLinks
    AllowOverride All

  <Directory /var/www/>
    Options FollowSymLinks MultiViews
    AllowOverride All
    Order allow,deny
    allow from all

  <FilesMatch \.php$>
    SetHandler "proxy:unix:/var/run/php/php7.4-fpm-www-roundcube.sock|fcgi://localhost/"

  SetEnvIf HTTPS on HTTPS=on

  RemoteIPHeader X-Forwarded-For


Create the Nginx Server Block

Next we create the Nginx server block:

nano /etc/nginx/sites-available/

and enter the following:

server {

        listen 443 ssl http2;
        listen [::]:443 ssl http2;


        modsecurity on;
        modsecurity_rules_file /etc/nginx/modsec/main.conf;

        ssl_certificate /etc/letsencrypt/rsa-certs/;
        ssl_certificate_key /etc/letsencrypt/rsa-certs/;
        ssl_certificate /etc/letsencrypt/ecc-certs/;
        ssl_certificate_key /etc/letsencrypt/ecc-certs/;
        ssl_trusted_certificate /etc/letsencrypt/ecc-certs/;

        rewrite "^/([0-9]{4})/([0-9]{2})/([0-9]{2})/(.*)$" /$4 permanent;

        # Strong SSL settings and secure headures
        include /etc/nginx/custom-config/ssl.conf;
        include /etc/nginx/custom-config/header.conf;

        # Set Gzip
        include /etc/nginx/custom-config/optimisation.conf;

        # Nginx Bad Bot Blocker Includes
        include /etc/nginx/bots.d/ddos.conf;
        include /etc/nginx/bots.d/blockbots.conf;

        # Block Bad Countries
        if ($allowed_country = no) {
            set $blockreason '[geo_blocked]';
            return 403;

        # 7g Firewall 
        if ( $bad_querystring !~* "\[OK\]" ) {
            set $blockreason $bad_querystring;
            return 403;

        if ( $bad_request !~* "\[OK\]" ) {
            set $blockreason $bad_request;
            return 403;

        if ( $bad_request_method !~* "\[OK\]" ) {
            set $blockreason $bad_request_method;
            return 403;

        # Restrict locations
            deny all;
        location ~ ^/(bin|SQL|tmp|logs)/ {
            deny all;

        # Everything else
        location / {
            client_max_body_size 50M;
            include /etc/nginx/custom-config/proxy.conf;
            #proxy_buffering off;


Create the Certificates

We now create the SSL certificates:

Issue an RSA certificate and install to a custom location

~/ --issue --dns dns_cloudns -d --keylength 4096 --key-file /etc/letsencrypt/rsa-certs/ --ca-file /etc/letsencrypt/rsa-certs/ --cert-file /etc/letsencrypt/rsa-certs/ --fullchain-file /etc/letsencrypt/rsa-certs/ --pre-hook "mkdir -p /etc/letsencrypt/rsa-certs/" --post-hook "find /etc/letsencrypt/rsa-certs/ -name '*.pem' -type f -exec chmod 600 {} \;" --renew-hook "find /etc/letsencrypt/rsa-certs/ -name '*.pem' -type f -exec chmod 600 {} \; -exec service nginx reload \;"

and issue an ECC certificate

~/ --issue --dns dns_cloudns -d --keylength ec-384 --key-file /etc/letsencrypt/ecc-certs/ --ca-file /etc/letsencrypt/ecc-certs/ --cert-file /etc/letsencrypt/ecc-certs/ --fullchain-file /etc/letsencrypt/ecc-certs/ --pre-hook "mkdir -p /etc/letsencrypt/ecc-certs/" --post-hook "find /etc/letsencrypt/ecc-certs/ -name '*.pem' -type f -exec chmod 600 {} \;" --renew-hook "find /etc/letsencrypt/ecc-certs/ -name '*.pem' -type f -exec chmod 600 {} \; -exec service nginx reload \;"

Create the PHP Pool

Create the PHP Pool:

nano /etc/php/7.4/fpm/pool.d/www-roundcube.conf

Enter the following:

; pool name

; Unix user/group of processes
; will be used.
user = www-roundcube
group = www-roundcube
listen.owner = www-data = www-data
listen.mode = 0660

; Socket to which the Apache will connect
listen = /run/php/php7.4-fpm-www-roundcube.sock

apparmor_hat = roundcube

pm = ondemand

pm.max_children = 5
pm.process_idle_timeout = 10s
pm.max_requests = 500

php_admin_value[allow_url_fopen] = On
php_admin_value[allow_url_include] = On
php_admin_value[memory_limit] = 512M
php_admin_flag[output_buffering] = Off
php_admin_value[max_execution_time] = 1800
php_admin_value[max_input_time] = 3600
php_admin_value[post_max_size] = 50M
php_admin_value[upload_max_filesize] = 50M
php_admin_value[upload_tmp_dir] = /var/www/
php_admin_value[sys_temp_dir] = /var/www/
php_admin_value[max_file_uploads] = 100
php_admin_flag[session.cookie_secure] = True
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen,show_source,putenv
;php_admin_value[open_basedir] = /var/www/

Enable Sites and PHP Pool

Enable the Apache Virtualhost:


Enable the Nginx Server Block

ln -s /etc/nginx/sites-available/ /etc/nginx/sites-enabled/

Restart PHP-FPM

service php7.4-fpm restart

Reload the web servers:

service apache2 reload && service nginx reload

Check configurations:

nginx -t
apachectl configtest

Complete Installation

Go to the Roundcube installer page:

Hopefully, all the requirements will be met. Click next and in the 2nd page, enter the database details for the database you created earlier. Enter the appropriate settings too in the IMAP and SMTP sections.

The IMAP default host should be:


and the smtp server should be:


The IMAP port should be 993 and the SMTP port 587.

In the plugins section, enable all the plugins except autologon, database attachments, and redundant attachments.

Once that’s done, click create config and proceed to the next step which is to check the config.

Now go to the main page at and login. If all is well, delete the install folder:

rm -r /var/www/

Removing Sensitive Information from Email Headers

Run the following command to create a header check file:

nano /etc/postfix/smtp_header_checks

Enter the following into the file, then save.

/^User-Agent.*Roundcube Webmail/            IGNORE

Edit the Postfix main configuration file.

nano /etc/postfix/

Add the following line at the end of the file:

smtp_header_checks = regexp:/etc/postfix/smtp_header_checks

Save then run the following command to rebuild the hash table:

postmap /etc/postfix/smtp_header_checks

Reload Postfix

systemctl reload postfix

Configure the Password Plugin in Roundcube

Roundcube includes a password plugin that allows users to change their password from the webmail interface.

First we copy the distributed config file to a new file:

cp /var/www/ /var/www/

Edit the config file:

nano /var/www/

Find and change the following lines:

$config['password_db_dsn'] = 'mysql://postfix:postfixadmin_database_password@';
$config['password_query'] = "UPDATE mailbox SET password=%D,modified=NOW() WHERE username=%u";
$config['password_strength_driver'] = 'zxcvbn';
$config['password_zxcvbn_min_score'] = 5;
$config['password_minimum_length'] = 8;
$config['password_algorithm'] = 'dovecot';
$config['password_dovecotpw'] = '/usr/bin/doveadm pw -r 5';
$config['password_dovecotpw_method'] = 'ARGON2I';
$config['password_dovecotpw_with_method'] = true;

Fix permissions on the file:

chown www-roundcube:www-data /var/www/
chmod /var/www/

Configure the Enigma GPG Plugin in Roundcube

Create a directory a level down from the web root:

mkdir /var/www/

Fix permissions on the new folder:

chown -R www-roundcube:www-roundcube /var/www/
chmod g+s /var/www/

Edit the Roundcube config file:

nano /var/www/

Add the following value under the $config['plugins'] section

$config['enigma_pgp_homedir'] = '/var/www/';

Restart Apache2

service apache2 restart

Configure Logrotate

We need to create a logrotate config in order to rotate the Roundcube log files:

nano /etc/logrotate.d/roundcube

Enter the following:

/var/www/*.log {
        rotate 7
                service apache2 reload > /dev/null

Configure the PHP-FPM Apparmor Changehat

We now need to create a PHP-FPM changehat apparmor profile for Roundcube:

nano /etc/apparmor.d/php-fpm.d/roundcube

and enter the following:

  ^roundcube flags=(attach_disconnected, complain) {
    #include <abstractions/base>
    #include <abstractions/dovecot-common>
    #include <abstractions/nameservice>
    #include <abstractions/openssl>
    #include <abstractions/php>
    #include <abstractions/ssl_certs>

    signal receive peer=php-fpm7.4,

    deny /usr/share/{phpmyadmin,postfixadmin}/** r,
    deny /var/www/{,}/** r,

    /usr/bin/dash Px -> roundcube-dash,

    /run/mysqld/mysqld.sock rwlk,
    /usr/share/dovecot/protocols.d/ r,
    /usr/share/dovecot/protocols.d/*.protocol r,
    @{run}/php/php7.4-fpm-www-roundcube.sock rwlk,
    owner /var/www/** r,
    owner /var/www/*.log w,
    owner /var/www/** rw,
    owner /var/www/* rw,
    owner /var/www/*/ rw,
    /etc/mime.types r,

Create a special profile for Dash:

nano /etc/apparmor.d/roundcube.dash

Enter the following:

#include <tunables/global>

profile roundcube-dash flags=(attach_disconnected,complain) {
  #include <abstractions/base>
  #include <abstractions/mysql>

  network inet6 stream,
  network inet stream,

  /bin/dash mr,
  /etc/nsswitch.conf r,
  /etc/passwd r,
  /proc/*/fd/ r,
  /run/php/php7.4-fpm-www-roundcube.sock rw,
  /usr/bin/dash r,
  /usr/bin/gpg rix,
  /usr/bin/gpg-agent rix,
  /usr/bin/gpg-connect-agent rix,
  /usr/bin/gpgconf rix,
  /var/www/** rw,
  owner /var/www/** rwl,
  owner /var/www/*/random_seed rwk,

Reload apparmor:

service apparmor reload

Configure the Apache2 Apparmor Changehat

We now need to create an Apache2 changehat apparmor profile for Roundcube:

nano /etc/apparmor.d/apache2.d/roundcube-a2

and enter the following:

^roundcube-a2 flags=(attach_disconnected) {
    #include <abstractions/apache2-common>
    #include <abstractions/base>
    #include <abstractions/nameservice>
    #include <abstractions/php>

    capability setuid,
    capability setgid,

    # for log writing (could be abstracted)
    /var/log/apache2/access.log w,
    /var/log/apache2/error.log w,

    # Socket access
    /run/php/php7.4-fpm-www-roundcube.sock wr,

    # Access to standard Roundcube files
    /var/www/ r,
    /var/www/** r,

    # Deny access to these locations
    deny /usr/share/phpmyadmin/** r,
    deny /usr/share/postfixadmin/** r,
    deny /var/www/{vendor/bin,bin,SQL,tmp,logs}/ r,
    deny /var/www/{vendor/bin,bin,SQL,tmp,logs}/** r,
    deny /var/www/**.sh rwx,
    deny /var/www/**.{php,sample} r,

    # Deny access to Bash
    deny /bin/bash r,
    deny /bin/dash r,


Reload apparmor:

service apparmor reload