Server Setup for Debian 12

For my future self (Hello 👋 from the past. How did you break the server this time?) here are the steps for my Debian 12 server setup, the server this very site is hosted on.

Login with Root

ssh root@your_server_ip

Install Package Updates

apt update
apt upgrade

Set Time Zone

dpkg-reconfigure tzdata

# Select your time zone (continent and region)
# and confirm each eith `Enter`.

If tzdata is not available:

timedatectl list-timezones

# Navigate with `Page Up` and `Page Down` to find the
# correct time zone, then exit with `q`.

# Set the correct time zone, e.g.
timedatectl set-timezone 'Europe/Berlin'

To check the current time on the server, run the following command:

date

Set Custom Hostname

hostnamectl set-hostname your-custom-hostname

Add the Custom Hostname to System’s hosts File

nano /etc/hosts

Add two lines (one for IPv4, one for IPv6) with the respective public IP of your server followed by your custom hostname (and optionally your server’s FQDN (Fully Qualified Domain Name)):

127.0.0.1 localhost.localdomain localhost
203.0.113.10 your-custom-hostname.example.com your-custom-hostname
2600:3c01::a123:b456:c789:d012 your-custom-hostname.example.com your-custom-hostname

Harden SSH Configuration

sudo nano /etc/ssh/sshd_config
...
# Change SSH port number (port 22 is the default).
Port your_ssh_port
...
# Disable root login.
# Make sure to only change this once you have
# another user configured (see next step).
PermitRootLogin no
...
# Disable password authentication.
PasswordAuthentication no
...
sudo systemctl restart sshd

Create Limited User Account

adduser your_user

# Add the user to the sudo group
adduser your_user sudo

Setup SSH for New User

On local machine:

# If the directory doesn't exist on your local system yet, create the directory first
mkdir -p ~/.ssh
sudo chmod -R 700 ~/.ssh && chmod -R 600 ~/.ssh/*

cd ~/.ssh

ssh-keygen -a 100 -t ed25519 -f ssh_key_name
ssh-copy-id -i ssh_key_name -p your_ssh_port your_user@your_server_ip

To automatically use key (and optional different port) when connecting:

nano ~/.ssh/config

And in there add entry:

Host your_server_ip
  IdentityFile ~/.ssh/ssh_key_name
  IdentitiesOnly yes
  Port your_ssh_port

Now to connect to server with user:

ssh your_user@your_server_ip

Setup Firewall (UFW)

sudo apt install ufw

Configure Firewall Rules

! Before enabling the firewall, make sure to allow the currently active SSH port (22 if you haven’t changed it), otherwise you will be locked out of your server upon enabling the firewall.

sudo ufw allow your_ssh_port

# To check what applications are available as rules
sudo ufw app list
# To allow a certain application with its default ports
sudo ufw allow "<the name of the app>"

# Check the status
sudo ufw status

Enable the Firewall

sudo ufw enable

Setup Nginx

sudo apt install nginx
sudo systemctl enable nginx
sudo systemctl start nginx
systemctl status nginx

# Update the firewall rules, either "Nginx Full", "Nginx Http" or "Nginx Https"
sudo ufw allow "Nginx Full"

Visiting the server IP in your browser should give you the Nginx test page.

http://your_server_ip

Enable Brotli Compression for Nginx

sudo apt install libnginx-mod-http-brotli-filter

To enable it by default for all sites, go to /etc/nginx/nginx.conf and in the http block add the following lines:

brotli on;
brotli_comp_level 6;
brotli_types text/xml image/svg+xml application/x-font-ttf image/vnd.microsoft.icon application/x-font-opentype application/json font/eot application/vnd.ms-fontobject application/javascript font/otf application/xml application/xhtml+xml text/javascript  application/x-javascript text/plain application/x-font-truetype application/xml+rss image/x-icon font/opentype text/css image/x-win-bitmap;

Then restart Nginx

systemctl restart nginx

Setup Nginx Site

Add new directory to host the site from.

sudo mkdir -p /var/www/your_domain/web
sudo chown -R $USER:$USER /var/www/your_domain/web
nano /var/www/your_domain/web/index.html
<html>
  <head>
    <title>Welcome to your_domain</title>
  </head>
  <body>
    <h1>
      Success! Your Nginx server is successfully configured for
      <em>your_domain</em>.
    </h1>
    <p>This is a sample page.</p>
  </body>
</html>

Add configuration file for the new site.

sudo nano /etc/nginx/conf.d/your_domain.conf
server {
    server_name your_domain www.your_domain;

    listen 80;
    listen [::]:80;

    root /var/www/your_domain/web;
    index index.html index.htm index.nginx-debian.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

Check configuration syntax

sudo nginx -t
sudo systemctl restart nginx

Setup SSL for the new site

Install certbot via the EPEL repository.

sudo apt install certbot python3-certbot-nginx

Generate certificates for the site.

sudo certbot --nginx -d your_domain -d www.your_domain certonly

Update the nginx configuration for the site.

sudo nano /etc/nginx/conf.d/your_domain.conf
server {
    server_name your_domain www.your_domain;

    listen 443 ssl;
    listen [::]:443 ssl;

    ssl_certificate /etc/letsencrypt/live/your_domain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    root /var/www/your_domain/web;
    index index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }
}


server {
    server_name your_domain www.your_domain;

    listen 80;
    listen [::]:80;

    return 301 https://$host$request_uri;
}

To redirect all non-www requests to the www domain, use this server configuration instead.

server {
    server_name www.your_domain;

    listen 443 ssl;
    listen [::]:443 ssl;

    ssl_certificate /etc/letsencrypt/live/www.your_domain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.your_domain/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    root /var/www/your_domain/web;
    index index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }
}

server {
    server_name your_domain;

    listen 443 ssl;
    listen [::]:443 ssl;

    ssl_certificate /etc/letsencrypt/live/your_domain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    return 301 https://www.$host$request_uri;
}

server {
    server_name your_domain www.your_domain;

    listen 80;
    listen [::]:80;

    return 301 https://www.$host$request_uri;
}

Note that this server configuration uses a different certificate for the non-www and www domain respectively. Alternatively, you can use the same certificate for both domains by defining the second domain as Subject Alternative Name.

Test the renewal process.

sudo certbot renew --dry-run

Usually the certbot installation comes with automated renewals directly configured. This can be confirmed by checking either the crontab or systemd timers for the certbot renew command.

For systemd timers:

sudo systemctl list-timers --all

In case the systemd timer is inactive, you can enable and start it manually with:

sudo systemctl enable certbot-renew.timer
sudo systemctl start certbot-renew.timer
sudo systemctl status certbot-renew.timer

For crontab:

sudo crontab -e

TODO

This post is Tagged with  the following keywords: