Written 10/11/2024 , updated at 12/11/2024
To start off some public notes and documenting what I learn, I decided to tackle setting up a fresh Ubuntu VPS to host my personal website, a side-project, and a small Minecraft server.
Introduction
While I’ve been coding on and off since about 2013 (nearly half my life now), I spent way too much of that time following a YouTube tutorial series, copy and pasting code, and not doing any real learning. During my time in college, I learned the value of just parsing the standard library, learning my tools and learning how to use them well, and just creating something is a far greater than I initially thought it was.
Around the start of 2024 I got hooked on the IndieWeb movement (introduced by the website jvt.me, not sure how I initially found it, unfortunately) and creating my own personal garden. That, coupled with the trend now to move from the cloud back to self-hosting applications (which is ironic given that I was on a project at work to move from self-hosting to the cloud…), I really wanted to learn things on my own without relying on heavy infrastructure like Vercel and the like.
With that I thought it would be a golden oppertunity to take the time to document setting up a VPS, securing it, while learning a bit about the web on the way. I will be setting all of this up on an OVH Comfort VPS running a fresh Ubuntu 24.04 installation. Before getting into it, I should define a few requirements.
Requirements
- Two domains, HTTPS, www
I have two domains, taxborn.com and braxtonfair.com. I have the username taxborn everywhere, so I essentially ‘brand’ myself as such. I did also want to have my name as a domain in case I ever decided to ditch the name, but today is not that day. I want to redirect all traffic from braxtonfair.com -> https://www.taxborn.com.
I want to ensure all traffic is encrypted with SSL, and force www. I went back and forth on whether to have www.taxborn.com or the non-prefixed taxborn.com. Looked at a couple [1] [2] resources and determined it was mostly bikeshedding and went with www.taxborn.com.
- Easy deployments and updates
I got to learn a lot about GitHub Actions at work during my internship.
- Understand my setup
VPS Cleanup
I have created notes on setting up a VPS already, and can be found here.
Installing Nginx
These commands are pulled from nginx’s instructions.
As of writing, the default apt repository has Nginx version 1.24.0, but checking the nginx website downloads shows a recent mainline version 1.27.2. I want to use the latest mainline version (not the stable, seems recommended to use mainline), so we need to change which repository apt uses to download Nginx:
Mainline version (recommended)
If you want the latest and greatest, you can use the mainline version of Nginx. This branch may also have experimental modules and new bugs.
Stable version
The only difference in this process is the 3rd command, setting the url to http://nginx.org/packages/ubuntu over http://nginx.org/packages/mainline/ubuntu.
Creating a site to host
*TODO: I want to explore using Docker, K8s, or Earthly, so I may come back and update this part for that.*
I already have a website I want to host, taxborn.com. This is a website created with Astro. On their website, Astro describes itself as:
Astro is a JavaScript web framework optimized for building fast, content-driven websites.
I have had a great experience with the framework, and love the fast outputs. It has the option to output a statically-generated website (SSG), or output to a server (in my case, Node.JS).
Start by creating a /var/www
folder: sudo mkdir -p /var/www
. This is where our website will live. We then need to setup permissions for the www folder. sudo chown -R ubuntu:www-data /var/www
, which allows the ubuntu
user and the www-data
user group to own this directory. Also, add the ubuntu
user to the www-data
group sudo usermod -aG www-data ubuntu
.
We can then cd /var/www
, and clone the repo we want to use (e.g. git clone https://github.com/taxborn/taxborn.com
).
Getting Node
I’ve recently been enjoying using fnm, so will install the latest Node version through that. fnm use --install-if-missing 22
.
I eventually want to use deploy keys and GitHub Actions CI/CD so this way of updating the website will change.
Then it’s just a matter of installing the dependencies and building: npm i && npm run build
. The built website is in the dist/
folder. To run my website, I execute the command node dist/server/entry.mjs
, which results in:
The website is running on the VPS on port 4321. We can’t access this quite yet, we are going to configure Nginx as a reverse proxy (cloudflare has a nice article about this) as a TLS termination proxy to handle https traffic.
This allows our Node application to not have to handle https communication itself, allowing some performance gains (I should really benchmark this claim, though).
DNS
I already have my domains (taxborn.com and braxtonfair.com) verified with OVH and pointing at this server. Currently not being proxied through Cloudflare but will turn that on later. TTL is set to 1 day and I set both A and AAAA records.
Configuring Nginx
The configuration files for Nginx (at least in Ubuntu, likely in other distributions) lay in /etc/nginx
. Let’s make sure we have 2 folders available to us, sudo mkdir /etc/nginx/sites-available
and sudo mkdir /etc/nginx/sites-enabled
. We can create a config for taxborn.com with sudo vim /etc/nginx/sites-available/www.taxborn.com
:
Let’s now enable the site www.taxborn.com by creating a symlink sudo ln -s /etc/nginx/sites-available/www.taxborn.com /etc/nginx/sites-enabled/
, and ensure our nginx configuration loads the site sudo vim /etc/nginx/nginx.conf
I plan to thoroughly document these settings soon, in a seperate note. To ensure your configuration is valid, run sudo nginx -t
, and if all is well, restart with sudo systemctl restart nginx
.
From there, we need to ensure our firewall allows traffic over port 80, really easy with ufw: sudo ufw allow 80/tcp
and sudo ufw reload
. Now I can access my website at http://15.204.234.44.
Generating an SSL certificate
Certbot AKA LetsEncrypt has been my go-to for this for a while. Before we get too far, let’s ensure that we also allow traffic over port 443, with sudo ufw allow 443/tcp && sudo ufw reload
.
Installing certbot is easy, sudo apt install certbot python3-certbot-nginx
. From there, we can generate an SSL certificate with the following command: sudo certbot --nginx -d taxborn.com -d www.taxborn.com
. Let certbot do it’s thing, and you should see something like the following at the end:
The file at /etc/nginx/sites-available/www.taxborn.com
should look a bit different now:
A little messy, I am going to clean that up a bit and add some comments…
Much better… again test the config with an sudo nginx -t
and reload with sudo systemctl restart nginx
.
SSL
Mozilla provides a helpful resource for some reasonable SSL defaults in an Nginx config. Let’s incorporate those recommendations.
They also have configurations for various server software like Apache, HAProxy, etc…
Testing our configuration
There are a few resources I use to test my website configuration.
- MDN’s HTTP Observatory - ensure proper headers are setup.
- Qualys SSL Labs - test SSL configuration to ensure a secure setup.
- Probely Security Headers - another headers check.
Conclusion
That’s about what it took for me to deploy my web application on a VPS with Nginx and secured with HTTPS. I will issue a few more updates to this as I learn more but that got me to a place where I was initially happy.