Server Setup

Very basic server setup for Ubuntu server.

Initial update

apt update
apt upgrade

Create a user

adduser myuser

Add the user to the sudo group if necessary:

adduser myuser sudo

SSH configuration

Upload the user’s public key:

ssh -i ~/.ssh/mykey myuser@host

Set up /etc/ssh/sshd_config:

PermitRootLogin no
StrictMode yes
PubkeyAuthentication yes
PasswordAuthentication no
X11Forwarding no  # Unless you intend to use GUI on the server.

Restart the SSH daemon:

systemctl restart sshd

Enable automatic security updates

sudo apt install unattended-upgrades

Then set up /etc/apt/apt.conf.d/10periodic:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";

Make sure unattended security updates are enabled in /etc/apt/apt.conf.d/50unattended-upgrades:



apt install fail2ban


apt install ufw

Allow SSH access and enable the firewall:

ufw allow ssh
ufw enable

Then allow additional ports as required by the services running on the host. For example, for HTTP:

ufw allow 80
ufw allow 443


apt install logtwatch

Send emails daily:

nano /etc/cron.daily/00logwatch

/usr/sbin/logwatch --output mail --mailto --detail high

Change the address from which the email appears to come:

nano /usr/share/logwatch/dist.conf/logwatch.conf

MailFrom = server-name


apt install apparmor apparmor-profiles

Make sure to install apparmor-profiles, which includes profiles for everyday applications.


Run nginx -t at any time to validate the site configuration files.

Modern SSL/TLS Configuration

SSL Config - Generate a config for the web server.

SSL Test - Test the server.



add_header X-Frame-Options DENY always;

DENY is a better default. Use SAMEORIGIN if the web application requires iframes of its own.


A few tweaks to improve performance. Write the performance directives into a re-usable snippet:


Then, in the site configuration, include it in the relevant section:

server {
  include /etc/nginx/snippets/perf.conf;


Instruct the browser to cache assets for some period of time. I play with css/js more often than not, so I set the cache time to 1 day. Images can be cached for one week.

location ~* \.(jpg|png)$ {
        expires 7d;
        add_header Cache-Control "public";

location ~* \.(css|js)$ {
        expires 1d;
        add_header Cache-Control "public";


Enable compression to reduce data transfer sizes. Compressing small files often bloats them, so adjust the lower bound (1k in the example).

gzip on;
gzip_min_length 1024;


Create a git system user that will own the git repositories:

adduser --system --shell /bin/bash git

Create a directory to store the git repositories:

mkdir /opt/git

Make main the default branch when creating repositories:

git config --global init.defaultBranch main

Create repositories, e.g.:

mkdir /opt/git/repo
cd /opt/git/repo
git init --bare
chown -R git /opt/git/repo

Give other users access to the git repositories by adding their keys to the git user’s authorized_keys (a simple, good-faith setup):

mkdir /home/git/.ssh
touch /home/git/.ssh/authorized_keys # Add keys here
chown -R git /home/git/.ssh
chmod 700 /home/git/.ssh
chmod 400 /home/git/.ssh/authorized_keys


Ubuntu Server Guide

Debian Unattended Upgrades


A Regular Expression Tester for NGINX and NGINX Plus

Understanding Nginx Server and Location Block Selection Algorithms

How to Configure Cache-Control Headers in NGINX