Table of Contents
WordPress
Self-Hosting

In this blog I will go over how I setup WordPress (this website you’re now viewing) on my home server. I will detail the steps I took to install Ubuntu with Docker, setup the domain, configure proxy, and the initial setup of WordPress.

Prerequisites

Domain

You'll need a domain if you want your WordPress to be accessible over the internet.

Proxy

(Optional) You can setup your own proxy manager, or use something like Cloudflare tunnels.

Ubuntu Server

Either setup as a virtual machine, or on bare metal.

  • Server Configuration

Installing Ubuntu Container

01. My site is currently running as a container within my Proxmox server.

> To Start I right-clicked on my server I called 'hulk' and selected 'Create CT'.

02. I then filled out the 'CT ID', 'Hostname', and setup a password for the user 'root'.

> Clicked 'Next'.

03. Proxmox has container templates you can download directly, I decided on using Ubuntu 22.04.

> The benefits of using a container is it uses very little resources, and is able to boot up quickly

04. I selected which storage I want this container to be installed on.

> I named my storage 'Incredible' and 'Mindless' for 'Incredible/Mindless Hulk' and because I'm not good at naming.

05. I gave the container 1 core.

> Which has been more than enough. It averages around .18% usage, and only jumps to 15% when I'm saving a draft, or updating a post.

06. I left it at 512MB of RAM, it hasn't needed much more right now. I might upgrade it to 1GB in the future.

07. I changed the Bridge from vmbr0, to vmbr2, since vmbr2 is my LAN port.

> I gave it a VLAN Tag of 50 because I setup a VLAN dedicated to my website on my pfSense router. (So I can properly segment traffic, and add necessary firewall rules, etc).

> The IP addresses shown are not the ones I actually setup, but I did make sure to set a static IP.

08. I left DNS settings at default.

09. Finally, clicked 'Finish'.

10. Once the output says 'TASK OK' then Proxmox has finished setting up the container.

11. I then went to 'Options' and made sure to make the server 'Start at boot'.

12. I right-clicked on the created container and selected 'Start'.

13. Then on the left-side menu I went to 'Console' so I can start configuring the Ubuntu server.

Server Configurations

14. After logging in as root, I ran the following command to update packages.

				
					sudo apt-get update && sudo apt upgrade -y
				
			
 apt-get update   – updates the package lists for upgrades and new package installations.
 apt upgrade   – will upgrade all currently installed packages.
 -y   – will automatically answer ‘yes’ to any prompts.

15. Next I installed some more necessary packages.

				
					sudo apt-get install apt-transport-https ca-certificates curl software-properties-common -y
				
			

 apt-transport-https   – allows the package management utility to handle HTTPS sources.
 ca-certificates   – is used to verify the authenticity of HTTPS connections.
 curl   – is used to be able to download files from the internet.
software-properties-common   – adds useful scripts for managing software repositories.

16. Next I added a user, because it's good practice to not use 'root'.

				
					sudo adduser nesto
				
			

17. Then I added the user to the sudo group, that way they have sudo permissions to run commands.

				
					sudo usermod -aG sudo nesto
				
			

18. Then I installed OpenSSH so I can remote in using MobaXterm (you can use Putty if you'd like)/

				
					sudo apt install openssh-server
				
			

19. The SSH service will usually start after installing it, but I like to make sure by running the following command.

				
					sudo systemctl start ssh
				
			

20. I won't go over how to do this, but I used MobaXterm to SSH into this Ubuntu server. Mainly because it's easier to copy and paste commands into the CLI.

Installing Docker

21. Running the following command will download the official GPG key for the Docker repository.

				
					curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
				
			

-fsSL   – Is a combination of flags:
 -f   – tell ‘curl’ to not output anything if the HTTP status code indicates an error..
 -s   – tells ‘curl’ to hide the progress meter and error messages.
 -S   – tells ‘curl’ to show error messages if it fails.
 -L   – tells ‘curl’ to handle redirects.

22. Then I added the Docker repository with this command.

				
					sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
				
			

 deb   – indicates that the repository is for binary packages.
 [arch=amd64]   – means the repository is for 64-bit software.
 $(lsb_release -cs)   – returns the codename for the Ubuntu server we installed and currently running commands on
stable tells the repository to use the latest stable version for Docker.

23. Updated packages again.

				
					sudo apt-get update
				
			

24. Now I can install Docker Community Edition with the following command.

				
					sudo apt-get install docker-ce -y
				
			

25. To test if Docker is installed correctly, I ran the following which will pull an image from the internet and output data to tell you if Docker is working.

				
					sudo docker run hello-world
				
			

26. (Optional) Docker should start on it's own after installing, but you can double check by running the following command to start the service.

				
					sudo systemctl start docker
				
			

27. Important: I wanted Docker to run upon startup, that way if there is a power outage, I won't have to manually start it.

				
					sudo systemctl enable docker
				
			

28. I then added the user I created to the 'Docker' group, so that I have permission to run docker commands.

				
					sudo usermod -aG docker ${USER}
				
			

Installing Docker-Compose

29. Now I downloaded Docker-Compose, and placed it in the '/usr/local/bin' directory.

				
					sudo curl -L "https://github.com/docker/compose/releases/download/v2.7.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
				
			

-L   – Tells ‘curl’ to follow any redirects.
 $(uname -s) and $(uname -m)   – are used to automatically fill in the system’s kernel name and machine hardware name (e.g. ‘Linux’ and ‘x86_64).

30. Then I made the Docker-Compose binary executable.

				
					sudo chmod +x /usr/local/bin/docker-compose
				
			

31. You can check if the installation was successful by running the following command.

				
					docker-compose --version
				
			
  • Wordpress Installation

Configuring YAML File

32. I then created a directory called 'my_wordpress' to keep things organized, and then moved into that directory.

				
					mkdir my_wordpress && cd my_wordpress
				
			

33. Then I created the yaml file.

				
					nano docker-compose.yml
				
			

34. I copy and pasted the following and made minor changes detailed below.

				
					version: '2.1'

services:
   wordpress:
     image: wordpress:latest
     ports:
       - 8077:80
     restart: always
     environment:
       WORDPRESS_DB_HOST: db
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: passw0rd!
       WORDPRESS_DB_NAME: wordpress
     links:
       - db:db
   db:
     image: mysql:5.7
     volumes:
       - db:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: passw0rd!
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: passw0rd!

volumes:
    wordpress:
    db:
				
			

 ports: 8077:80   – Maps port 8077 on the host to port 80 on the container, allowing you to access the WordPress site via port 8077 on your Docker host.

Make sure the username and password for WORDPRESS_DB the same for MSQL.

To save it you press ‘CTRL+X’, press ‘y‘, and then hit ‘Enter‘.

35. Finally to install I ran the following.

				
					docker-compose up -d
				
			

36. To check if it's running you can enter the following.

				
					sudo docker ps
				
			
  • Domain Setup

Cloudflare DNS

37. I created a free account on Cloudflare, and navigated to DNS > Records.

38. I then created an A Record and entered my public IP into the 'IPv4 address' field.

39. Then I went back to 'Overview' from the left-side menu so I can make note of my API Zone ID and Account ID.

I'll need these 2 ID's, the Global API Key, and I need to create an API key for when I setup HAProxy.

Then I clicked on ‘Get your API token‘.

40. Then clicked on 'Create Token'.

41. I used the 'Edit zone DNS' template.

42. I added the following under 'Permissions' and picked a random day many years in the future.

Then I saved the API token for future use.

43. For the last key I clicked on 'View' and made note of the key.

ACME Certificate

44. I then logged into my pfSense router and went to Services > Acme Certificates.

45. Then clicked 'Add'.

46. I gave the certificate a distinctive name, since I'll be referencing it later. A short description if you’d like.

Acme Account - leave default.
Private key - leave default.
OCSP Must Staple - leave default.
Preferred Chain - leave default.

47. Domainname - enter the domain name I setup on Cloudflare.
Method - select DNS-Cloudflare
Key - the global API key I viewed earler..
Email - the email I used to create my Cloudflare account.
Token - the API Token I created earlier.
Account ID - my account ID I copied earlier.
Zone ID - the zone ID I copied earlier.

48. Once created, I clicked on 'Issue/Renew'.

HAProxy

49. Next, I navigated to Services > HAProxy.

50. I have to create the backend first.

51. I like to end my names with -Backend so I can differentiate.

Name - the domain name I setup on Cloudflare.
Forwardto - Address+Port.
Address - local IP address of the Ubuntu server.
Port - the port I specified in the yaml file.
CA - I selected the Acmecert I just created.
Client Certificates - I selected the cert I created.

52. Now I setup the frontend.

53. I also end these with -Frontend to differentiate.

Listen address - keep it as WAN address.
Port - I used 443 since we just created an SSL certificate.

Scroll down a bit.

54. I configured the ACLs next.

Name - name the ACL.
Expression - selected 'Host matches'.
Value - the domain name.

Scroll down some more.

55. Now we setup the action for that ACL.

Action - Use Backend.
Condition acl names - reference the ACL we just created.
backend - select the backend I created earlier.
Default Backend - select the same backend from earlier.

56. For SSL Offloading, make sure you select the Certificate for this domain on both of these locations. Then finally saved. Almost done.

  • Wordpress Setup

Finish Setup

57. Now I navigated to the domain name I setup earlier.

It's important to use the domain name when doing this setup, and not the actually IP, or else you'll get SSL errors and CSS files won't load.

58. All of this can be changed later, just make sure to remember the username and password.

59. Finally, log in.

Increase File Upload Size

57. I SSH'd back into my server.

To find the name of the docker container run the following command, and make note of the name

				
					docker ps --formate "{{.Names}}"
				
			

35. I need to copy the .htaccess file from within the Docker container to my system.

Make sure to replace /path/to/.htaccess with the actual path to the .htaccess file in the Docker container. Usually, this would be /var/www/html/.htaccess.

				
					docker cp container_id_or_name:/path/to/.htaccess ./.htaccess
				
			

35. Edit the file with nano.

				
					nano .htaccess
				
			

35. Added the following to the top and saved and exited (you can adjust the size to your needs).

				
					php_value upload_max_filesize 256M
php_value post_max_size 256M
php_value memory_limit 256M
				
			

35. I then copied the file back into the Docker container.

				
					docker cp ./.htaccess container_id_or_name:/path/to/.htaccess
				
			

35. Finally, restarted the Docker container to apply the changes.

				
					docker restart container_id_or_name