How to Deploy Multiple Wordpress Sites Using Virtualmin and Ansible on Ubuntu 16.04

A common usage of a Vultr virtual server is to host WordPress websites. This guide shows you how to automate the configuration of a virtual server from scratch (using Ansible) and deploy multiple independent WordPress websites (using Webmin/Virtualmin). Virtualmin/Webmin is a graphical user interface that allows you to manage deployment of multiple virtual server accounts on the same machine (complete with LAMP/LEMP stack). Virtualmin is very similar to cPanel and Plesk, and in this tutorial we’ll be using the free GPL edition.
After initial setup of the Vultr server and installation of Virtualmin, you can very quickly setup multiple virtual servers from within the Virtualmin interface and directly install WordPress on that virtual server complete with its own domain name.

In this tutorial, instead of manually entering a long list of commands, we’ll instead use Ansible. Ansible is a python based automation tool which allows you to reliably and repeatedly automate server tasks. This means once you’ve followed this tutorial, you’ll be able to deploy another server in the same way with just a couple of commands.


  • At least one Fully-Qualified Domain Name and access to the DNS records
  • A Vultr account

Step 1 – Installing Ansible on your local machine

Install Ansible on your local machine or another server.

mkdir ansiblecd ansiblevirtualenv envsource env/bin/activatepip install ansible

Step 2 – Generate SSH keys and deploy server

Ansible works by logging into your server via SSH. SSH access is most secure if we use keys rather than a password. Let’s first generate a public and private key pair.

mkdir ssh_keysssh-keygen -t rsa -b 2048 -f ./ssh_keys

In the ssh_keys directory there will now be two files, ssh_keys and ssh_keys is your private key file and should be kept safe. You can now open the, which contains the public key.

Login to the Vultr web dashboard and click Deploy New Server.

Select a region, Server type (Ubuntu 16.04), Server size, and then in part 6 (SSH keys), click Add New. On the next page paste your public key and give it a name, and click Add SSH key. Finally ensure that key is selected and click Deploy now.
Once the server has finished deploying you’ll be shown its ip address. You’ll need to login to your domain name’s DNS server and point it to this address.

Step 3 – Create a basic Ansible configuration

Ansible’s automation files are called roles. We’ll first setup the directory structure (inside the ansible directory you just created in step 1), and the basic files.

mkdir -p group_vars roles/common/tasks/ roles/common/handlerstouch hosts group_vars/all deploy.yml roles/common/handlers/main.yml

Edit the hosts file to contain the following, substituting the ip address for the server you just created. Ansible uses python 2, which Ubuntu 16.04 doesn’t have installed by default. In the hosts file we tell Ansible to use python 3.

[common] ansible_python_interpreter=/usr/bin/python3

Edit the deploy.yml file to contain the following. We are going to be using the root user.

- name: apply common configuration to server  hosts: all  user: root  roles:    - common

Edit the /group_vars/all file to contain the following. These variables tell Ansible the location of your SSH keys, swap file parameters, your Fully Qualified Domain Name and the root password. Please remember not to include the file in source control as it contains your password in clear text.

ssh_dir: ./ssh_keysswap_file_path: /swapfileswap_file_size: 1Gswappiness: 1hostname: example.comnew_password: YOUR_PASSWORD_HERE

Edit the common/handlers/main.yml file to contain the following.

- name: restart sshd  service: name=ssh state=restarted

Step 4 – Create Ansible tasks for basic server setup

Ansible automation is easier to understand if we break it down into tasks. Let’s create files for each of our tasks in the process.

cd roles/common/taskstouch hosts main.yml setup.yml users.yml ufw.yml swap.yml virtualmin.yml

main.yml should point to each file containing the Ansible commands, so edit it to contain the following.

- include: setup.yml- include: users.yml- include: ufw.yml- include: swap.yml- include: virtualmin.yml

The first step in setting up a new server is to update the repo cache and set the timezone. Edit the common/handlers/setup.yml file to contain the following.

- apt: update_cache=yes  sudo: yes- name: set timezone to Europe/London  timezone:    name: Europe/London

Now, we’ll give the root user a password (which we will need to access the virtualmin web interface), but disable password logins over SSH (since we are using the more secure keys method of authentication). Edit users.yml to contain the following.

- name: Change passwd  user: name=root password={{ new_password | password_hash('sha512') }} update_password=always- name: Disable SSH password login  lineinfile: dest=/etc/ssh/sshd_config regexp="^#?PasswordAuthentication" line="PasswordAuthentication no"  notify: restart sshd

For security, we need a firewall. We’ll use the Uncomplicated Firewall to allow SSH access on port 22, web access on port 80 and secure web access on port 443. Edit the ufw.yml file to contain the following.

- name: Set default firewall policy to deny all  become: True  ufw: state=enabled direction=incoming policy=deny  tags: firewall- name: enable SSH in firewall  ufw: rule=allow port=22  sudo: yes- name: enable HTTP connections for web server  ufw: rule=allow port=80  sudo: yes- name: enable HTTPS connections for web server  ufw: rule=allow port=443  sudo: yes- name: enable firewall  ufw: state=enabled  sudo: yes

Optionally, you can include a swap file. This is essential if your server has less than 2GB RAM to avoid out of memory crashes. Edit swap.yml to contain the following.

- name: Set swap_file variable  set_fact:    swap_file: "{{swap_file_path}}"  tags:    - swap.set.file.path- name: Check if swap file exists  stat:    path: "{{swap_file}}"  register: swap_file_check  tags:    - swap.file.check- name: Create swap file  command: fallocate -l {{swap_file_size}} {{swap_file}}  when: not swap_file_check.stat.exists  tags:    - swap.file.create- name: Change swap file permissions  file: path="{{swap_file}}"        owner=root        group=root        mode=0600  tags:    - swap.file.permissions- name: Format swap file  sudo: yes  command: "mkswap {{swap_file}}"  when: not swap_file_check.stat.exists  tags:    - swap.file.mkswap- name: Write swap entry in fstab  mount: name=none         src={{swap_file}}         fstype=swap         opts=sw         passno=0         dump=0         state=present  tags:    - swap.fstab- name: Turn on swap  sudo: yes  command: swapon -a  when: not swap_file_check.stat.exists  tags:    - swap.turn.on- name: Set swappiness  sudo: yes  sysctl:    name: vm.swappiness    value: "{{swappiness}}"  tags:    - swap.set.swappiness

Step 5 – Add Ansible task for virtualmin setup

Virtualmin has its own installer file which can be downloaded and run by Ansible. Here we are using the minimal install (LINK). The additional items are to configure the MySQL server password which is not set when installed by Virtualmin. We need to temporarily stop MySQL and add the authentication directory prior to changing the password. Edit virtualmin.yml to contain the following.

- name: download virtualmin install script  get_url: >    url=    dest=/root/    mode=0755- name: virtualmin install (takes around 10 mins) you can see progress using $ sudo tail -f /root/virtualmin-install.log  tags: non-idem  shell: ~/ --force --hostname {{ hostname }} --minimal --yes  args:    chdir: /root- name: temp stop mysql  service:    name: mysql    state: stopped- name: change owner (and group) of mysqld dir  file:    path: "/var/run/mysqld"    state: directory    owner: mysql    group: mysql- name: virtualmin set mysql password  shell: virtualmin set-mysql-pass --user root --pass {{ new_password }}- name: restart mysql  service:    name: mysql    state: started

The Ansible role is now finished and we’re ready to deploy.

Step 6 – Perform install with Ansible

From the ansible folder, we can now simply run the following command, and Ansible will carry out all of the tasks we have created automatically. The first time you connect you’ll get an SSH key warning, just type “yes” at the prompt.

ansible-playbook deploy.yml --private-key=ssh_keys/ssh_keys -i hosts

If we wish to use another server, we can simply change the ip address in the hosts file and run that command again to complete exactly the same setup.

Step 7 – Virtualmin post-installation wizard

The installation is complete and we can now go to (use the ip address of your server). Your browser will issue a security warning because the certificate is self signed, so click advanced and add an exception. You will be presented with a login page. The username is root, and the password is the one you entered into group_vars/all file in step 3. The first time you enter Virtualmin you’ll be presented with the post-installation wizard. You can either go through these settings manually or click cancel to accept the defaults.

Step 8 – Create a server and install WordPress

To get your first WordPress server up and running, from the Virtualmin dashboard click Create Virtual Server. You’ll need to enter a domain name, description and an administrators password. The domain name should be different from the Virtualmin fully qualified domain name, and you’ll need to point the DNS record to the ip address of your server.

Click Create Server. Once Virtualmin has finished creating your server, click Install Scripts on the left hand menu. Select WordPress, click Show install options, and on the following page choose the location of the WordPress install. Just choose At top level and click Install Now.

That’s all you need to do – you can complete the WordPress install by visiting your (where is this virtual servers domain name). If your DNS records haven’t propagated yet you can go to Services > Preview Website from the Virtualmin menu.

You can repeat this step multiple times to create multiple WordPress sites all on the same Vultr server.

Want to contribute?

You could earn up to $300 by adding new articles

Submit your article
Suggest an update
Request an article

No comments

Powered by Blogger.