Deploying & Securing a Webservice on Linux (Ubuntu VPS, NGINX, SSL)

3 min read Page Views

Project Overview

This project demonstrates hands-on Linux server administration, security hardening (SSH, UFW), web server (NGINX) configuration, DNS management, and SSL implementation by deploying my portfolio site on a public DigitalOcean VPS.


Stack Used

  • Hugo (static site generator)
  • Ubuntu 25.04 (DigitalOcean VPS)
  • NGINX (web server)
  • Certbot (Let’s Encrypt SSL)
  • UFW (firewall)

1. Bought a Domain

I purchased tyronerhoden.com to serve as my public-facing domain. You need this to attach DNS records later and issue an SSL certificate.

boughtdomain


2. Installed Git and Hugo Locally

I installed Git and Hugo on my Windows machine to generate and manage my site.

git --version
hugo version
Breakdown
  • git --version: Confirms Git is installed.
  • hugo version: Verifies Hugo is working, which will generate the static site.

cmd output


3. Built Hugo Site and Tested Locally

I created a new Hugo project, added the void theme, and ran a local dev server to validate layout and structure.

hugo new site tyrportfolio
cd tyrportfolio
git init
git submodule add https://github.com/adityatelange/hugo-void themes/void

Done in git bash.


4. Built Static Files for Deployment

hugo

This compiled the site into the public/ directory, ready for upload to the VPS. Compile


5. Created and Secured My VPS (DigitalOcean)

  • Deployed Ubuntu 25.04 droplet
  • Region: Toronto
  • Plan: $5/mo basic (1 vCPU, 1GB RAM, 25GB SSD)
  • Added SSH key for secure access (no password login)
ssh-keygen -t ed25519 -C "tyrone@tyronerhoden.com"
Breakdown
  • ed25519: Chosen for smaller key size and stronger security than RSA.
  • SSH key uploaded to DigitalOcean dashboard to avoid password login (best practice).

4droplet_config


6. Connect to VPS

ssh root@138.197.129.145

5…8.server_access


7. Hardened the Server

adduser tyrone
usermod -aG sudo tyrone

Then copied SSH keys to new user:

mkdir -p /home/tyrone/.ssh
cp /root/.ssh/authorized_keys /home/tyrone/.ssh/
chown -R tyrone:tyrone /home/tyrone/.ssh
chmod 700 /home/tyrone/.ssh
chmod 600 /home/tyrone/.ssh/authorized_keys

8. Installed and Started NGINX

sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginx

Visited the droplet IP to confirm NGINX was running.

9…nginx_running


9. Configured Domain and NGINX Block

Set up /etc/nginx/sites-available/tyronerhoden.com with the proper root directory and domain. Enabled it with a symlink and reloaded NGINX.

sudo systemctl reload nginx
Breakdown
This reloads NGINX to apply the new site configuration without killing the process. This is safer than a full restart in live setups.

10. Uploaded My Hugo Site to the VPS

scp -r public/* tyrone@138.197.129.145:/var/www/tyronerhoden.com

But I ran into permission issues:

⚠️ Error: SCP Permission Denied

What went wrong:
The target directory /var/www/tyronerhoden.com was owned by www-data, blocking my scp upload.

🛠️ How I fixed it:

  1. SSH’d into the VPS.
  2. Ran:
    sudo chown -R tyrone:www-data /var/www/tyronerhoden.com
    sudo chmod -R 755 /var/www/tyronerhoden.com
    
  3. Re-uploaded with scp. Worked perfectly.

11. Enabled HTTPS with Certbot

sudo certbot --nginx -d tyronerhoden.com -d www.tyronerhoden.com

16certbot


12. Final Permissions and Reload

Reset file permissions back to secure default:

sudo chown -R www-data:www-data /var/www/tyronerhoden.com
sudo systemctl reload nginx

13. Enabled UFW Firewall

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

Final Result

Fully secured, live Hugo portfolio. Hosted on an optimized, hardened Linux VPS. This wasn’t theoretical, this was a hands-on, real-world deployment.


Lessons Learned

  • Server permissions will bite you if not planned
  • Static site builds are fast, but full-stack web delivery still involves SSH, DNS, and SSL
  • Small mistakes (like domain mismatch) compound later, so it’s better to slow down and verify early
Last updated on 2025-07-25