RegularDev

In this post, I’m excited to share how I built my own blog-experiment-lab-website from scratch using a VPS (Virtual Private Server) and Docker. I wanted to understand how these projects work by doing everything myself. This article might help you if you’re looking to do something similar.

Starting Off with a VPS

First, you’ll need a VPS. If you want your project to have a domain name, you can either buy one or use a free one from your VPS provider. If you buy one, you’ll need to set it up to point to your VPS. Also, make sure to open the ports you need and secure the ones you don’t with a Firewall. A good security step is to use something like Fail2Ban.

When you get your VPS, you’ll start with a root user account. It’s not safe to use this for everything, so you’ll want to make a new user. I did this with a simple command:

adduser username

You can check if the new user is created with another command:

grep '^username' /etc/passwd

Setting Up Docker

Next up is installing Docker. You can find step-by-step instructions on its official website. Don’t forget to add your new user to the Docker group with a command:

usermod -aG docker your_username

Then, refresh your group membership with this command:

newgrp docker

Getting Your Project Ready

Now for the project part. I used Astro for my static website. You need a Dockerfile to tell Docker how to build your project’s image:

# BUILD STAGE
FROM node:lts-alpine as build  # use nodejs alpine image and assign it name "build" to use it in production stage
WORKDIR /app  # create work directory
COPY package*.json ./  # copy package.json and package-lock.json from project to work directory
RUN npm install  # install dependencies
COPY . .  # copy source code from project to work directory
RUN npm run build  # build project
 
# PRODUCTION STAGE
FROM nginx:stable-alpine as production  # use nginx alpine image
COPY --from=build /app/dist /usr/share/nginx/html  # using name of the image from previous stage copy bundle to nginx serving directory
EXPOSE 80  # expose port 80 so you can connect to nginx outside the container
CMD ["nginx", "-g", "daemon off;"]  # run nginx

If you’re using a different package manager than npm, remember to include its .lock file by adding an extra COPY step in the Dockerfile. You can learn why turning off “daemon” is important for nginx images here

Run it on VPS

To run your project on the VPS, you need a docker-compose.yml file. This file tells Docker how to set up your project to make it available from outside your VPS. For my Astro website, I needed three things: a reverse-proxy to manage incoming requests, a letsencrypt-companion for SSL certificates, and a container for my website:

services:
  nginx:
    container_name: nginx
    image: nginxproxy/nginx-proxy
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./nginx/html:/usr/share/nginx/html
      - ./nginx/certs:/etc/nginx/certs
      - ./nginx/vhost:/etc/nginx/vhost.d
    logging:
      options:
        max-size: "10m"
        max-file: "3"
 
  letsencrypt-companion:
    container_name: letsencrypt-companion
    image: jrcs/letsencrypt-nginx-proxy-companion
    restart: unless-stopped
    volumes_from:
      - nginx
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./nginx/acme:/etc/acme.sh
    env_file:
      - email.env
  frontend:
    container_name: frontend
    build: .
    env_file:
      - hosts.env

You’ll also need to create two files in the same folder as your docker-compose.yml:

email.env:

DEFAULT_EMAIL=test@mail.com

and hosts.env:

VIRTUAL_HOST=example.com
LETSENCRYPT_HOST=example.com

This setup lets you host your project on any VPS with Docker:

VPS containers scheme

Finally, to get your project running, just pull it to your VPS from your git repository, go to the project’s directory, and run a command:

docker compose up -d

The -d flag means it will run in the background, so you can close the terminal, and it will keep going.

Wrap-Up

With this setup, you can easily move your project to any server that has Docker. You can also start thinking about how to automatically deploy to your VPS whenever you make changes to your project. I’ll cover that topic in the next post. Stay tuned!