Stephen Gilmore

🧑‍💻 Setting up a new VPS for Django

Django June 24th, 2023 8 minute read.

I've done this a few times and taking this one to go a bit slower and clean up some notes.

Create the VPS instance

Set up an SSH alias on local computer

Execute nano ~/.ssh/config

In the file, add the following:

Host mysite
        HostName 123.12.12.1234
        User myuser

Save and now you'll be able to ssh in with ssh myuser@mysite instead of ssh [email protected]

Login and update the instance

sudo apt-get update
sudo apt-get upgrade
sudo apt-get full-upgrade
sudo apt-get autoremove

Install and set up UFW firewall

Install:

# Install ufw
sudo apt intall ufw

# Allow ssh
sudo ufw allow ssh

# Enable ufw
sudo ufw enable
sudo ufw reload
sudo ufw status

# optional - see a list of ufw apps
sudo ufw app list

Check and update SSH Permissions

# Open the SSH config file
sudo nano /etc/ssh/sshd_config
- PasswordAuthentication no To disable password authentication and require an SSH Key - PermitRootLogin no To disable root login (don't do until after setting up an alternate user)

There's a lot more available here in the article How to Harden OpenSSH

Then Restart the ssh service:

sudo systemctl restart sshd
Logout with logout and if you can ssh back in, you didn't lock yourself out!

Set up a non-root user

Create a new user with the adduser command

adduser myuser

Add the user to the sudo group with

usermod -aG sudo myuser

Copy the SSH key from the root user to your user

Create the folder for your user if it doesn't exist

mkdir /home/myuser/.ssh

Make the directory only executable by the user

chmod 700 /home/myuser/.ssh

Copy the authorized keys to the user

sudo cp /root/.ssh/authorized_keys /home/myuser/.ssh/authorized_keys

Change ownership to the user

sudo chown -R myuser:myuser /home/myuser/.ssh

Make it readable only by the current user

sudo chmod 600 /home/myuser/.ssh/authorized_keys

logout and try to ssh back in with the myuser user

Install rsync

rsync is very useful for pushing files up to the server.

sudo apt install rsync

Install caddy as a reverse proxy

We'll use this to route requests from the outside world to our static files and Django application. A few nice things about caddy: - Automatic certificates for https - Pretty easy to set up config and nice defaults like routing http to https

Following the stable release installation instructions here: Debian/Ubuntu/Raspbian instructions.

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https

curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg

curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list

sudo apt update

sudo apt install caddy

Check the status of caddy:

sudo systemctl status caddy
# 'q' to exit

Install golang-migrate

https://github.com/golang-migrate/migrate/blob/master/cmd/migrate/README.md

# Install the migrate CLI tool.
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.16.2/migrate.linux-amd64.tar.gz | tar xvz
mv migrate.linux-amd64 /usr/local/bin/migrate

# Check migrate installation
migrate -version

Allow http & https traffic through UFW

# Allow http & https
sudo ufw allow "WWW Full"

# Enable ufw
sudo ufw reload
sudo ufw status

Serve a test page from Caddy

# Edit the caddy file
sudo nano /etc/caddy/Caddyfile

Add...

{
    auto_https off
}

:80 {
    respond "hello world"
}

Restart Caddy and check the status

sudo systemctl restart caddy
sudo systemctl status caddy

In another terminal, use curl to test if it's working:

> curl server.ip.addres.here
hello world

Install Docker

Docker install using the repository

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Add the repository to Apt sources:
echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

Install the Docker Packages

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Verify installation is successful by running the hello-world image

sudo docker run hello-world

Post Docker Installation Configuration

Manage Docker as a non-root user

# Create a docker group
sudo groupadd docker

# Add your user to the group (admin as the default for a lightsail container)
sudo usermod -aG docker $USER

# Logout then log back in

# Verify you can run `docker` commands without `sudo`
docker run hello-world

Configure docker to start on boot with systemd

sudo systemctl enable docker.service
sudo systemctl enable containerd.service

To stop this behavior, use disable instead

sudo systemctl disable docker.service
sudo systemctl disable containerd.service

Consider a sudo reboot now to restart the VPS and verify all the services we just set up are running. Replace enable/disable with status to check them.

sudo systemctl disable docker.service
sudo systemctl disable containerd.service

Configure the local docker logging driver to rotate logs Edit the daemon.json file:

sudo nano /etc/docker/daemon.json

Add the following:

{
  "log-driver": "local",
  "log-opts": {
    "max-size": "10m"
  }
}

Serve a Django site from Caddy

sglmr.com {

        handle_path /media/* {
                root * /var/www/dg-media     
                file_server
        }

        handle_path /static/* {
                root * /var/www/dg-static     
                file_server {
                        precompressed br gzip
                }
        }

        handle {
                reverse_proxy 127.0.0.1:8000
        }

}

Install redis server for task queues and caching

Not needed if using redis through docker

Install redis to use as a task queue and cache for Django.

sudo apt install redis-server

Open the config file

sudo nano /etc/redis/redis.conf

Change the supervised setting to systemd

# change the supervised setting to `systemd`
supervised systemd

# uncomment the `save` setting to "" to disable persistence
# and also make sure appendonly is set to no
save ""
appendonly no

Restart redis to make sure the settings take effect:

sudo systemctl restart redis.service

Make sure redis is running:

sudo systemctl status redis

Test Redis

Open up the redis cli

redis-cli
Type ping and you should get a response PONG

Try adding a test record set test "Hello redis!". You should get an OK response.

Try to retrieve the value with get test. You should get the same Hello redis! message back.

Type exit to exit.

If trying to disable persistence, then we'll want to test that to. Restart your machine with sudo reboot now.

SSH back into the server. Check the redis status again

sudo systemctl status redis

Go back into the redis-cli

redis-cli

Try to get your test record again with get test. The response should be (nil).

We can also double check the config settings by using config get save and config get appendonly