How to Install Virtualmin on CentOS 7 With PHP 7 and NGINX

Table Of Contents

Share Article

I have a couple of sites that I am keeping on a VPS with the above configuration. In this article, I will present you all the steps and the configurations you need to make to have Virtualmin installed on a CentOS 7.6 VPS with PHP7.2 and NGINX as a webserver together with free SSL certificates from Let’s Encrypt.

Virtualmin is a great tool and is offering all you need from DNS, email, FTP, and server administration. It has a GUI interface from where you can manage everything easily and is very stable.

A. Steps to Install Virtualmin and Have Everything Configured.

1. Create The VPS

The first thing to do is to have your VPS ready and installed. I am using DigitalOcean for my sites as it has exactly what I need. You can check the review here: DigitalOcean Review

For a more affordable option to DigitalOcean, you can check this Hetzner Review.

To do this you login into DigitalOcean or create an account and you hit create, then you choose the image, plan, data center, name and you hit create. The name should be something like ns1.<domain> or vps.<domain>, etc

The size of the VPS is up to you but to have a couple of small sites hosted the 5$ plan can be enough ( without a lot of email processing) Also you can increase your size in the future if you need more.

2. Add a swap file, to do this you do:

After the creation process finishes you should receive an email with your VPS IP with the user name and password. The next thing you need to download Putty so you can connect to the VPS and run the commands.

DigitalOcean is not adding any swap file so you need to do this ( the Virtualmin install process can do this by default but I prefer to do it myself):

sudo fallocate -l 1G /swapfile
sudo dd if=/dev/zero of=/swapfile bs=1024 count=1048576
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile swap swap defaults 0 0' | sudo tee -a /etc/fstab

After you add the swap you can upgrade your machine to the latest version. to do that you just run:

yum update
yum upgrade

After you finish with the above you just reboot the machine and you are set to install Virtualmin

3. Install Virtualmin With NGINX

The install is very easy, you just need to install wget first so you can take the package and after just run the install with /root/install.sh -b LEMP

yum install wget
wget http://software.virtualmin.com/gpl/scripts/install.sh
sh /root/install.sh -b LEMP

▣▣▣ Phase 3 of 3: Configuration
[1/23] Configuring AWStats                                              [  ✔  ]
[2/23] Configuring Bind                                                 [  ✔  ]
[3/23] Configuring ClamAV                                               [  ✔  ]
[4/23] Configuring Dovecot                                              [  ✔  ]
[5/23] Configuring Firewalld                                            [  ✔  ]
[6/23] Configuring MySQL                                                [  ✔  ]
[7/23] Configuring NTP                                                  [  ✔  ]
[8/23] Configuring Net                                                  [  ✔  ]
[9/23] Configuring Nginx                                                [  ✔  ]
[10/23] Configuring ProFTPd                                             [  ✔  ]
[11/23] Configuring Procmail                                            [  ✔  ]
[12/23] Configuring Quotas
The filesystem / could not be remounted with quotas enabled.
You may need to reboot your system, and/or enable quotas in the Disk
Quotas module.                                                          [  ⚠  ]
[13/23] Configuring SASL                                                [  ✔  ]
[14/23] Configuring Shells                                              [  ✔  ]
[15/23] Configuring SpamAssassin                                        [  ✔  ]
[16/23] Configuring Status                                              [  ✔  ]
[17/23] Configuring Upgrade                                             [  ✔  ]
[18/23] Configuring Usermin                                             [  ✔  ]
[19/23] Configuring Webalizer                                           [  ✔  ]
[20/23] Configuring Webmin                                              [  ✔  ]
[21/23] Configuring Fail2banFirewalld                                   [  ✔  ]
[22/23] Configuring Postfix                                             [  ✔  ]
[23/23] Configuring Virtualmin                                          [  ✔  ]
▣▣▣ Cleaning up

[SUCCESS] Installation Complete!
[SUCCESS] If there were no errors above, Virtualmin should be ready
[SUCCESS] to configure at https://ns1.wpdoze.site:10000 (or https://159.65.108.159
10.46.0.5:10000).

After 10 minutes or so all the packages will be installed and you are ready to configure the installation.

4. Connect to the GUI and make final changes

https://159.65.108.159:10000/

After you need to access the UI with the server public ip and port 10000 over https and do the configuration changes. You log in with the root user and the passwords and you choose the below in the wizard:

  1. Preload Virtualmin libraries? –  No (less RAM used, slower UI and more CPU load)
  2. Run email domain lookup server? – No (less RAM used, slower mail and more CPU load)
  3. Run ClamAV server scanner?No (less RAM used, slower mail processing)
  4. Run SpamAssassin server filter? – No (less RAM used, slower mail processing)
  5. Run MariaDB/MySQL database server? – Yes (more RAM used, needed for most non-static websites)
  6. Run PostgreSQL database server? – No (less RAM used)
  7. MariaDB/MySQL configuration size -Medium system (512M) with regular MariaDB/MySQL use
  8. DNS set up: Primary nameserver: ns1.domain.com check the box Skip check for resolvability add a secondary DNS like:
    ns2.domain.com
  9. Password storage mode – Store plain-text passwords

After all this, you are set you just need to point your domain name to the DNS and create the custom DNS records.

5. Set UP the custom DNS Names and point the domain to the server

Now Virtualmin is installed and ready to host sites 🙂 as next actions, you need to go where you host your domain and create the custom DNS names pointing to the server IP. I am using NameCheap for this. And you need to go under the domain name and hit Manage -> Advanced DNS and add the ns1 and ns2 with the IP as below:

Now you have your custom nameserver set up and you just need to point your domain to use them from Domain Tab like below:

Video For Installing Virtualmin with Part A steps:

B. Some Extra Configurations:

In this area, we will do some extra configurations for multiple PHP versions, create a custom server template that will deploy WordPress automatically, and do some Nginx configurations for advanced performance.

1. Install Multiple PHP versions

Now as we have everything set up we can do some extra configs to have multiple PHP versions on our site, by default Virtualmin is installing PHP 5.6 with 7.0 you need to run the below to have multiple versions of PHP 7:

yum install centos-release-scl
yum install rh-php71 rh-php71-php-mysqlnd
yum install rh-php72 rh-php72-php-mysqlnd

2. Create a server templete to install wordpress automaticly

Next, I want to have a special server template and have some customization done for WordPress like installing it automatically when I add a domain and having the user and DB already added. Below are the steps to have this done:

  • Download WordPress and add it to a dir:

For this I will use a directory under /etc/wordpress, you can run the below commands to have everything set up:

# cd /etc/
# mkdir wordpress
# cd wordpress/
# mkdir public_html
# cd public_html/
# wget https://wordpress.org/latest.zip
# unzip latest.zip
# mv wordpress/* .
# rm -rf wordpress
  • Rename sample-wp-config.php to wp-config and replace the below values:
#cp wp-config-sample.php wp-config.php
  • Edit wp-config.php and use the below values so when you create a new account to have Virtualmin populate them by default.
define( 'DB_NAME', '${USER}' );

/** MySQL database username */
define( 'DB_USER', '${USER}' );

/** MySQL database password */
define( 'DB_PASSWORD', '${PASS}' );
  • Add the New Server Template in the Virtualmin GUI

Now you need to create the template to have it configured to do so go to Virtualmin > System Settings > Server Templates and hit create a template from the default settings:

Then give it name like WordPress and hit Create and Next. Now in the Home Directory use these configurations:

  • Skeleton directory for files – choose Directory and use the previous path where you stored WordPresss: /etc/wordpress
  • Substitute variables in contents? – Use : Yes
  • File patterns to only substitute: wp-config.php

Then hit Save

With this configuration, while choosing a WordPress template you will have by default WordPress installed.

3. Add the Below In the /etc/nginx/nginx.conf

Next to that, we want to do is to add some nginx configurations to have a better performance. All that is needed to be done is to add below in the /etc/nginx/nginx.conf. This can be done in the Virtualmin UI from the region: Webmin – Servers- Nginx Webserver – Edit Configuration File

Next, you need to add the below code:

  open_file_cache max=200000 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;
         ##
         # Gzip Settings
         ##
         gzip  on;
         gzip_comp_level 9;
         gzip_proxied expired no-cache no-store private auth;
        gzip_min_length 500;
         gzip_types text/plain text/css application/json  application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js;
         gzip_disable "MSIE [1-6]\.(?!.*SV1)";
    map $sent_http_content_type $expires {
    default                    off;
    text/html                  epoch;
    text/css                   max;
    application/javascript     max;
    ~image/                    max;
}

The configuration will look like this:

# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;
    open_file_cache max=200000 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;
         ##
         # Gzip Settings
         ##
         gzip  on;
         gzip_comp_level 9;
         gzip_proxied expired no-cache no-store private auth;
        gzip_min_length 500;
         gzip_types text/plain text/css application/json  application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js;
         gzip_disable "MSIE [1-6]\.(?!.*SV1)";
    map $sent_http_content_type $expires {
    default                    off;
    text/html                  epoch;
    text/css                   max;
    application/javascript     max;
    ~image/                    max;
}
    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
        
    }

# Settings for a TLS enabled server.
#
#    server {
#        listen       443 ssl http2 default_server;
#        listen       [::]:443 ssl http2 default_server;
#        server_name  _;
#        root         /usr/share/nginx/html;
#
#        ssl_certificate "/etc/pki/nginx/server.crt";
#        ssl_certificate_key "/etc/pki/nginx/private/server.key";
#        ssl_session_cache shared:SSL:1m;
#        ssl_session_timeout  10m;
#        ssl_ciphers HIGH:!aNULL:!MD5;
#        ssl_prefer_server_ciphers on;
#
#        # Load configuration files for the default server block.
#        include /etc/nginx/default.d/*.conf;
#
#        location / {
#        }
#
#        error_page 404 /404.html;
#            location = /40x.html {
#        }
#
#        error_page 500 502 503 504 /50x.html;
#            location = /50x.html {
#        }
#    }

	server_names_hash_bucket_size 128;
}

Now you just need to hit save

Extra Config Video

C. Create the First Virtual Server

We have everything we need to have our first website added into Virtualmin, until now we have:

  • Installed Virtualmin
  • Configure the DNS on to domain
  • Made some small changes to enhance virtualmin

Next, we need to add a Virtual Server so the domain to be added, to do so we hit: Virtualmin Create Virtual Server and input your domain name, server password and choose the server template created previously. In the enable features area you choose the below options and hit Create Server

The configuration will run and Virtualmin will add everything needed to have your first site. The site will be with SSL but it will have a self-signed one. If you visit the link you will see an exception, next we will need to add a LetsEncrypt SSL and make some Nginx configs:

  • Create a LetsEncypt SSL for your Nginx Website

All websites should have an SSL certificate and LetsEncypt helps you in generating a free one for you. You also need to renew it in 3 by 3 months. Virtualmin helps you generate the certificate automatically very easily and handle also the renewal. To have the certificate you go to Virtualmin – Server Configuration – SSL Certificate > Let’s Encrypt. You add in Months between automatic renewal – 2 ( so it can be renewed automatically) and hit Request Certificate:

Now you have the website with WordPress and the Let’s Encrypt certificate up and running. Next, you access the site URL with HTTPS and go thru the WordPress Installation process.

  • Additional Nginx Configs

To have all the traffic redirected from HTTP to HTTPS you need to add the below in /etc/nginx/nginx.conf. This can be done in the Virtualmin UI from the region: Webmin – Servers- Nginx Webserver – Edit Configuration File under the server for your domain

 if ($scheme = http) {
        return 301 https://$server_name$request_uri;
                     }

Add support for WordPress permalinks:


 location / {
                        try_files  $uri $uri/ /index.php?$args;
	 }		
			

Add HTTP2:

Nginx support http2 for faster loading of the websites under HTTPS, to enable this you just need to add into location the http2 like below

		listen 159.203.100.92:443 default ssl http2;

Then Save and Close and Apply Nginx Config

Then your server will look like this:

server {
		server_name wpdoze.site www.wpdoze.site;
		listen 159.203.100.92;
		root /home/wpdoze/public_html;
		index index.html index.htm index.php;
		access_log /var/log/virtualmin/wpdoze.site_access_log;
		error_log /var/log/virtualmin/wpdoze.site_error_log;
		fastcgi_param GATEWAY_INTERFACE CGI/1.1;
		fastcgi_param SERVER_SOFTWARE nginx;
		fastcgi_param QUERY_STRING $query_string;
		fastcgi_param REQUEST_METHOD $request_method;
		fastcgi_param CONTENT_TYPE $content_type;
		fastcgi_param CONTENT_LENGTH $content_length;
		fastcgi_param SCRIPT_FILENAME /home/wpdoze/public_html$fastcgi_script_name;
		fastcgi_param SCRIPT_NAME $fastcgi_script_name;
		fastcgi_param REQUEST_URI $request_uri;
		fastcgi_param DOCUMENT_URI $document_uri;
		fastcgi_param DOCUMENT_ROOT /home/wpdoze/public_html;
		fastcgi_param SERVER_PROTOCOL $server_protocol;
		fastcgi_param REMOTE_ADDR $remote_addr;
		fastcgi_param REMOTE_PORT $remote_port;
		fastcgi_param SERVER_ADDR $server_addr;
		fastcgi_param SERVER_PORT $server_port;
		fastcgi_param SERVER_NAME $server_name;
		fastcgi_param HTTPS $https;
		location ~ \.php$ {
			try_files $uri =404;
			fastcgi_pass unix:/var/php-nginx/155437756624142.sock/socket;
		}
		listen 159.203.100.92:443 default ssl http2;
		ssl_certificate /home/wpdoze/ssl.combined;
		ssl_certificate_key /home/wpdoze/ssl.key;
        if ($scheme = http) {
        return 301 https://$server_name$request_uri;
                            }
        location / {
                        try_files  $uri $uri/ /index.php?$args;
			       }
	

Video with Virtual Server Add

Become a CloudPanel Expert

This course will teach you everything you need to know about web server management, from installation to backup and security.
Leave a Reply

Your email address will not be published. Required fields are marked *

  1. Dragos,

    Sorry to bother you. I found the problem.

    My fault..

    Thanks,
    Julius

  2. Hello Dragos,

    So i set up a CentOS 8 server and followed the instructons here as usual but this time I am getting this error:
    “Failed to save configuration file : Configuration is invalid : nginx: [emerg] “map” directive is not allowed here in /etc/nginx/nginx.conf:60 nginx: configuration file /etc/nginx/nginx.conf test failed”

    I am doing some thing wrong or is the directive not supported on CentOS 8?

    map $sent_http_content_type $expires {
    default off;
    text/html epoch;
    text/css max;
    application/javascript max;
    ~image/ max;
    }

    Thanks for your help!
    Julius

  3. Thank you very much for this excellent tutorial. Please, can you kindly help with answers to these questions?
    1. As I watched your video tutorial, I realized you were able to login to webmin with the hostname: https://ns1.wpdoze.site:100000.
    What is the configuration in getting the hostname (server.example.com) as webmin login and not the ip address like: https://12.122.23.5:10000 (not my real ip address though).
    Note: I have setup my hostname (server.example.com) and everything is working fine except that I can’t login with the hostname.
    I have also setup the DNS (or A records) and have connected it to a Cloudflare account, and the two sites I have setup are all working perfectly.

    2. I was successful with getting a Letsencrypt for my hostname (server.example.com) but unfortunately, because the hostname is not resolvable, I can’t get it to work on the hostname.
    I have done all the things I have learnt from online tutorials so that, in the mean time, I can even use the Letsencrypt to secure my webmin ip address login like this: https://12.122.23.5:10000 but still receives a browser warning. Is there a better to get this fixed? Kindly let have your email address.

    3. Playing media files on my wordpress site gives this error: Media error: Format(s) not supported or source(s) not found Download File: http://stream.zenolive.com/km4bq88rhv5tv.
    I have tried to diable the “Always Use HTTPS” in my Cloudflare account but all to no avail.
    Please, could this error not be because of my nginx.conf file settings pasted below:
    user nginx;
    # Replace worker_process value with the # of CPUs you have
    worker_processes 1;
    error_log /var/log/nginx/error.log;
    pid /var/run/nginx.pid;

    events {
    worker_connections 1024;
    }

    http {
    charset utf-8;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    client_header_timeout 12;
    client_body_timeout 12;
    client_max_body_size 8m;
    client_header_buffer_size 1k;
    client_body_buffer_size 16k;
    large_client_header_buffers 2 2k;
    send_timeout 10;
    keepalive_timeout 65;
    reset_timedout_connection on;
    server_names_hash_max_size 1024;
    server_names_hash_bucket_size 1024;
    server_tokens off;
    add_header X-XSS-Protection “1; mode=block”;
    add_header X-Frame-Options “SAMEORIGIN”;
    ignore_invalid_headers on;
    connection_pool_size 112;
    request_pool_size 2k;
    output_buffers 2 8k;
    postpone_output 1460;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Logging Settings
    log_format gzip ‘$remote_addr – $remote_user [$time_local] ‘
    ‘”$request” $status $bytes_sent ‘
    ‘”$http_referer” “$http_user_agent” “$gzip_ratio”‘;

    access_log /var/log/nginx/access.log;
    access_log off;

    # Compression gzip
    gzip on;
    gzip_vary on;
    gzip_disable “MSIE [1-6]\.(?!.*SV1)”;
    gzip_proxied expired no-cache no-store private auth;
    gzip_min_length 500;
    gzip_comp_level 6;
    gzip_buffers 4 32k;
    gzip_types text/plain text/xml text/css text/js application/x-javascript application/xml image/png image/x-icon image/gif image/jpeg image/svg+xml application/xml+rss text/javascript application/atom+xml application/javascript application/json application/x-font-ttf font/opentype;

    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;
    open_file_cache max=200000 inactive=20s;
    open_log_file_cache max=1024 inactive=30s min_uses=2;

    # SSL Settings
    ssl_session_cache shared:SSL:10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;

    # Cache bypass
    map $http_cookie $no_cache {
    default 0;
    ~SESS 1;
    ~wordpress_logged_in 1;
    }

    Many thanks and patiently waiting for your kind response

  4. Thank you so much for these tutorials. Please can you do a tutorial on how to upgrade to Nginx to 1.14 and MariaDB 5.x to MariaDB 10.3?
    Thank you again!

    • Hello,

      Sure. I can do that.

      Thanks,
      Dragos

      • Hey Dragos,
        Thanks, man! I was able to get a new server setup with latest Nginx, PHP7.2 and MariaDB10.3. I don’t know if vhost will work fine but we’ll let you know.
        1 question: in the wp-config modification – what value can I use if I want to use the actual database name instead of ${USER} in line “define( ‘DB_NAME’, ‘database_name_here’ );”?
        Thanks again for your help!
        Julius

        • Hello Julius,

          For question 1 when you create an account with the default settings Virtualmin will create a database with the user name, that’s why you have the username there. You can add the database name also if you want with ${DB} you have the list here: https://www.virtualmin.com/documentation/system/template_variables
          If this is not working you can modify manually the file.

          For php.ini you can check the GUI in Services – PHP 7.0 Configs and there you have the path and option to edit the file from UI.

          • Thank you, Dragos! You’re amazing, man! I hope more people find out about you skills on Virtualmin. It’s a powerful system but is very confusing for most new users (especially those coming from CPanel/WHM).
            BTW, have you successfully installed SuiteCRM on Virtualmin/Nginx?

          • Thanks Julius. I have not tried but if SuiteCRM is a web app and it works on nginx it should work. The documentation should be followed on how to configure NGINX.