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.
Prerequisites
- 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 ansible
cd ansible
virtualenv env
source env/bin/activate
pip 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_keys
ssh-keygen -t rsa -b 2048 -f ./ssh_keys
In the ssh_keys
directory there will now be two files, ssh_keys
and ssh_keys.pub
. ssh_keys
is your private key file and should be kept safe. You can now open the ssh_keys.pub
, 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/handlers
touch 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]
192.0.2.1 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_keys
swap_file_path: /swapfile
swap_file_size: 1G
swappiness: 1
hostname: example.com
new_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/tasks
touch 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=http://software.virtualmin.com/gpl/scripts/install.sh
dest=/root/install.sh
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: ~/install.sh --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 https://192.0.2.1:10000
(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 http://example.net/wp-admin/install.php
(where example.net
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
Suggest an update
Request an article