Home   Back

Debian Server Configuration

This page is mostly for my own reference regarding the configuration and management of servers running Debian Linux.

Table Of Contents

SSH Configuration

Refer to here for information

Setting Up A Fileserver With ZFS

Modify APT Repositories

Add "contrib" to the all the lines in "/etc/apt/sources.list".

Install ZFS
# apt update
# apt install dpkg-dev linux-headers-$(uname -r) linux-image-amd64
# apt install zfs-dkms zfsutils-linux

Create ZFS Array With Disks

To create a mirrored pool, use the mirror keyword, followed by any number of storage devices that will comprise the mirror. Multiple mirrors can be specified by repeating the mirror keyword on the command line. The following command creates a pool with a mirror:

NOTE: when creating the zpools, it's better to use the /dev/disks/by-id names of the disks instead of the /dev/sdX names.

# zpool create $POOL_NAME mirror $DISK_1 $DISK_2

Create pool with RAID-Z array:

# zpool create $POOL_NAME raidz{1,2,3} $DISK_1 $DISK_2 $DISK_3 $DISK_{...}

Create Dataset


You can also specify a mountpoint when you create the dataset

# zfs create -o mountpoint=/$MOUNTPOINT $POOL_NAME/$DATASET_NAME

Share Pool/Dataset With Samba

Install Samba software

# apt install samba

Create a user for Samba. This is the user and password you use to access whatever is being shared to the network. The user must already exist as a Unix user on the system.

# smbpasswd -a $SAMBA_USER

You can use the "built in" smb sharing capabilities that ZFS has as long as you have Samba installed on the system. Set the "smbshare" property of the dataset to "on" with the zfs command:

# zfs set smbshare=on $POOL_NAME/$DATASET_NAME

Reminder: If you cannot access the network share, you probably don't have permission to the dataset on the server. Check where the dataset is mounted:

# zfs list

change the permissions of the directory so that the Samba user has full permission to the directory

Setting Up ZFS Event Daemon (ZED)

ZED monitors events generated by the ZFS kernel module. When a zevent (ZFS Event) is posted, ZED will run any ZEDLETs (ZFS Event Daemon Linkage for Executable Tasks) that have been enabled for the corresponding zevent class.

Edit /etc/zfs/zed.d/zed.rc with your favorite text editor

# vim /etc/zfs/zed.d/zed.rc

There are several lines in this file that you should uncomment and edit. Find the following lines in your file and make sure they match the ones below:

ZED_EMAIL_OPTS="ZED_EMAIL_OPTS="-s '@SUBJECT@' @ADDRESS@ -r youremail@example.com"

Configuring ClamAV

Installing ClamAV
# apt install clamav clamav-daemon

Clamscan Cron Job

Create a cron job in the root's crontab

# crontab -e

Add the following line to the file. This job will run a scan the third day of every month at 12am and will only send you an email if it finds any infected files:

0 0 3 * * mail -s "Fileserver ClamAV Scan Report" youremail@example.com -r youremail@example.com <<< $(clamscan --max-filesize=2000M --max-scansize=2000M --recursive=yes --infected /)

Restart your server.

Monitor Disks With Smartd

Install required packages

# apt install smartmontools

You can do manual tests on storage devices with the "smartctl" command, but you can configure it to do them automatically with smartd. The config file for smartd is at "/etc/smartd.conf".

You should see something similar to the following line in the beginning of the config file:

DEVICESCAN -d removable -n standby -m root -M exec /usr/share/smartmontools/smartd-runner

When smartd starts up, it first reads this config file to figure out what to do. When it comes across a "DEVICESCAN" line, it stops processing the file and scans for all SMART enabled storage devices attached to the computer. If you want to configure it for a particular drive then replace "DEVICESCAN" with a drive of your choice (/dev/sdX).

Here is an example line to use:

DEVICESCAN -a -o on -S on -s (S/../.././01|L/../01/./03) -m $EMAIL_ADDRESS

This will scan for all SMART enabled storage devices on the computer and then process the following for each drive found:

-a Equivalent to turning on all of the following Directives: '-H' to check the SMART health status, '-f' to report failures of Usage (rather than Prefail) Attributes, '-t' to track changes in both Prefailure and Usage Attributes, '-l error' to report increases in the number of ATA errors, '-l selftest' to report increases in the number of Self-Test Log errors, '-l selfteststs' to report changes of Self-Test execution status, '-C 197' to report nonzero values of the current pending sector count, and '-U 198' to report nonzero values of the offline pending sector count.

-o VALUE [ATA only] Enables or disables SMART Automatic Offline Testing when smartd starts up and has no further effect. The valid arguments to this Directive are on and off.

-S VALUE Enables or disables Attribute Autosave when smartd starts up and has no further effect. The valid arguments to this Directive are on and off. Also affects SCSI devices.

-s This is where you can set self-tests to run at scheduled times. This particular example will run a Short test every Saturday at 1am and a Long test on the first day of every month at 3am.

-m ADDRESS Send a warning email to the email address ADD if the '-H', '-l', '-f', '-C', or '-O' Directives detect a failure or a new error, or if a SMART command to the disk fails.

Configure SMTP Client For Email Alerts

Install required packages

# apt install bsd-mailx msmtp msmtp-mta

Configure msmtp

Next you’ll need to configure msmtp. There are two places to configure msmtp, you can setup a default configuration file (/etc/msmtprc) or you can setup a per user configuration file located under that user’s home directory. The file under the user directory is named .msmtprc If you decide to use a per user config file, you should chmod 600 that file. Unless you have a reason to setup a per user config file, setting up the default file /etc/msmtprc is simpler. You can also setup both a default config file, and then a per user config file if you prefer. Neither of these files already exist and you will have to create them. Here is an example of the configuration file:

# Set default values for all following accounts.
auth           on
tls            on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile        ~/.msmtp.log

account alerts
host smtp.youremail.com
port 587
from from@addresstosendfrom.com
user userforemail@user.com
password putpasswordhere

# Set a default account
account default : alerts

This file assumes you are using STARTTLS and the port number is 587. If you need to use TLS/SSL, but not use STARTTLS add the following entry:

tls_starttls off

Testing Mail Capabilities

Perform a test email with the following command:

echo test | mail -s "test subject" $EMAIL_ADDRESS

Unattended Upgrades

Install the required packages:

# apt install unattended-upgrades apt-listchanges
Configure Unattended-Upgrades

The default config file for unattended-upgrades is at "/etc/apt/apt.conf.d/50unattended-upgrades". Comment out the following line to receive email alerts:

Unattended-Upgrade::Mail "$EMAIL_ADDRESS or $USER";

You should make sure to uncomment the following line in the "/etc/apt/apt.conf.d/50unattended-upgrades" file:

//      "${distro_id}:${distro_codename}-updates";

Make sure that the following lines exist exactly as they appear in the "/etc/apt/apt.conf.d/20auto-upgrades" file to be sure that unattended upgrades are enabled

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

Reminder: If the "/etc/apt/apt.conf.d/20auto-upgrades" file does not exist then run dpkg-reconfigure to create it:

# dpkg-reconfigure -plow unattended-upgrades
Configure Apt-Listchanges

Below is an example of a configuration for the file for "/etc/apt/listchanges.conf". Change the value for "email_address" to get alerts for what packages were installed by unattended-upgrades






If The Above Does Not Work

If you are having trouble configuring the unattended upgrades doing it the way I have outlined above, you can just add a simple cron job.

Enter into the root crontab:

# crontab -e

Enter or paste the following at the bottom of the file:

0 * * * * /usr/bin/apt-get update && /usr/bin/apt-get --with-new-pkgs upgrade -y

This cron job will update the repo and upgrade any packages at the start of every hour.

NOTE: This will upgrade every package including the kernel. If you don't want to upgrade the kernel, then remove the "--with-new-pkgs" option.

Encrypting Entire Hard Drive With LUKS

Ensure you have "cryptsetup" installed

# apt install cryptsetup

Ensure that the "dm-mod" module is loaded into the kernel:

# modprobe dm-mod

Make a partition the size of the entire drive with fdisk:

# fdisk /dev/sd*

Setup encryption on that partition and pick a good passphrase to use:

# cryptsetup -v --cipher serpent-xts-plain64 --key-size 512 --hash whirlpool --use-random --verify-passphrase luksFormat /dev/sd*{1}

Open(unlock) the encrypted partition. The actual block storage device will be available at "/dev/mapper/$NAME"

# cryptsetup luksOpen /dev/sd*{1} $NAME

Create a filesystem on the unlocked partition to store files on it:

# mkfs.ext4 /dev/mapper/$NAME

Now mount to directory of your choice and store files on it:

# mount /dev/mapper/$NAME /mnt

To properly close the encrypted volume:

# umount /dev/mapper/$NAME

# cryptsetup luksClose /dev/mapper/$NAME

Configure UFW Firewall

Install Required Packages

# apt install ufw

You can install the GUI version with (optional):

# apt install gufw

Enable it to start it and allow it to start on boot

# ufw enable

Set the default to deny all incoming traffic

# ufw default deny incoming

Set the default to allow all outgoing traffic

# ufw default allow outgoing

Allow the SSH service as incoming traffic like this:

# ufw allow ssh

Allow port range like this:

# ufw allow 1000:2000/{tcp,udp}

Allow IP address like this:

# ufw allow from

Delete a rule

# ufw delete allow {$SERVICE, $PORTS, $IP_ADDRESS}

Reload the firewall after adding or deleting rules

# ufw reload

Check the current rules:

# ufw status verbose

Setup Email Server

This is a more condensed version of how to setup an email server. Refer to here for a detailed version of how to do it.

Adding Certificates For Mail Domain

Create a dummy nginx config file for your mail domain with your text editor of choice

# vim /etc/nginx/sites-available/mail

Copy or type the following into the file:

server {
	listen 80 ;
	listen [::]:80 ;

	root /var/www/mail;

	index index.html index.htm index.nginx-debian.html;

	server_name mail.$YOUR_DOMAIN www.mail.$YOUR_DOMAIN;

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

Link the file to "sites-enabled"

# ln -s /etc/nginx/sites-available/mail /etc/nginx/sites-enabled/mail

Reload nginx

# systemctl reload nginx

Generate certificates with cerbot

# certbot --nginx

Setting Up The Software

If you have Postfix or Dovecot installed previously, completely uninstall it and delete all related files.

Setup the email software with a script. Get the script from Github

# git clone https://github.com/LukeSmithxyz/emailwiz

Enter the "emailwiz" directory and execute the script.

NOTE: On installation of Postfix, select "Internet Site" and put in TLD without the "mail." before it.

# sh emailwiz.sh

If the script is successful it will generate some TXT records that you are supposed to enter in your domains DNS records. You should see something like this:

mail._domainkey		TXT	v=DKIM1; k=rsa; p=...

 _dmarc			TXT	v=DMARC1; p=none; rua=...; fo=1;

@			TXT	v=spf1; mx a:mail.$YOURDOMAIN -all

The left part is the "host" part of the TXT record and the right part is the "value" of the TXT record. Input these into your DNS records.

Adding Users To Receive Mail

Add a user on the server, add them to the "mail" group and give them a password. This is the passord they use to log in remotely through a mail client.

# useradd -m -G mail $USER
# passwd $USER

To allow an existing user to receive mail, add them to the "mail" group

# usermod -a -G mail $USER

Email Server Settings

If you want to access your mail remotely through a client like Thunderbird, here is the server information:

Testing Your DNS Records

mxtoolbox.com is a great site for testing various E-mail server configurations to verify that your server is working properly. mail-tester.com is a good site for testing your spam score of your emails.

Setup Web Server With HTTPS

This is a more condensed version of how to setup the webserver with the certificates. Refer to here for a detailed version of how to do it

Install the required packages

# apt install nginx python3-certbot-nginx

Create config file for your site with a text-editor of your choice

# vim /etc/nginx/sites-available/$FILENAME

Copy or type the following into the file:

server {
	listen 80 ;
	listen [::]:80 ;

	root /var/www/$DIRECTORY_NAME;

	index index.html index.htm index.nginx-debian.html;

	server_name $YOUR_DOMAIN www.$YOUR_DOMAIN;

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

Link the file to the "sites-enabled" directory

# ln -s /etc/nginx/sites-available/$FILENAME /etc/nginx/sites-enabled/$FILENAME

Create the root directory if you haven't already that you specified in the config file above. This is where you will put the files you want nginx to serve to clients.

# mkdir /var/www/$DIRECTORY_NAME

Reload nginx

# systemctl reload nginx

Check the journal for any errors. If you have any, its probably due to an error in the config file

# systemctl status nginx

Generate certificates for the site with certbot. It will prompt you with some questions. Use option 2 to redirect HTTP to HTTPS.

# certbot --nginx

Virtualization With QEMU/KVM

There are a couple pieces to the Linux virtualization puzzle that we need: QEMU, KVM, Libvirt and its associated tools (virsh, virt-install, virt-manager). KVM is a module that is already included in the mainline Linux kernel so we only have to download the other things. Technically, you only need QEMU and KVM to run virtual machines however, it is a bit tricky to manage virtual machines with just QEMU commands. That is where Libvirt and its associated tools steps in.

The package names can vary on each distribution, but here are the packages for Debian:

Installing Required Packages
# apt install qemu-system libvirt-clients libvirt-daemon-system virtinst qemu-utils

If installing on a headless server, you can choose to not install all the graphical related packages:

# apt install --no-install-recommends qemu-system libvirt-clients libvirt-daemon-system virtinst qemu-utils

Add your user to libvirt group to use virtual machines as non-root

# adduser $USER libvirt

User-Specific And System Wide VMs

By default, if virsh is run as a normal user it will connect to libvirt using "qemu:///session" URI string. This URI allows virsh to manage only the set of VMs belonging to this particular user. To manage the system set of VMs (i.e., VMs belonging to root) virsh should be run as root or with "qemu:///system" URI:

# virsh --connect qemu:///system list --all

Creating Virtual Machines

The easiest way to create VMs is through a GUI tool such as virt-manager. If you don't have a GUI on your server because you are running it headless, you can create VMs using the CLI based tools which are virsh and virt-install

Create a VM with virt-install. Here is an example:

# virt-install \
-n myDebianVM \
--description "Test VM with Debian 11" \
--os-type=Linux \
--os-variant=debian10 \
--ram=2048 \
--vcpus=2 \
--disk path=/var/lib/libvirt/images/myDebianVM.img,bus=virtio,size=10 \
--graphics none \
--cdrom /var/debian-11.0.0-amd64-netinst.iso \
--network bridge:br0 \

The virt-install command will essentially create an XML file which contains the entire configuration for a particular VM. In the above virt-install command the parameters have the following meaning:

Viewing VM Consoles On Headless Servers

When you first create a VM, the operating system must be installed which means you will have to access the console to go through the installation process just as you would on a normal physical computer. This is a bit of a problem on a headless server since it has no GUI, so then how can we get to a VMs console?

One of the ways is to use virt-viewer and connect to the hypervisor host remotely over SSH. So basically we install virt-viewer on a separate desktop computer that does have a GUI and then connect to the hypervisor host with SSH and tunnel over that.

Make sure that you have virt-viewer installed on the separate desktop computer and SSH configured on your hypervisor host server. Then, execute the following command but plug in your own information:

NOTE: You must make sure that you use "--graphics vnc" when creating the VM with virt-install. If you don't give it that parameter then this method will not work.

# virt-viewer --connect qemu+ssh://user@host/system $VM_NAME

With the above, you’ll have to enter your SSH password twice – first to establish the connection to the hypervisor and secondly to establish a tunnel to the VM’s VNC/SPICE session.

Managing VMs

Once again, the easiest way to manage VMs is with a GUI tool such as virt-manager. Your other option is to use the CLI tools like virsh if you are doing this on a headless server with no GUI.

A VMs configuration can be modified by editing it's XML file that was generated by the virt-install command. We can easily edit a specific VMs configuration with this command:

# virsh edit $VM_NAME

You can use the virsh command to start and stop virtual machines. VMs can be generated from virt-install as mentioned above. For more details visit the libvirt page.

Start a VM:

# virsh start $VM_NAME

Gracefully shutdown a VM:

# virsh shutdown $VM_NAME

If a VM hangs when attempting to shutdown, you can force it with:

# virsh destroy #VM_NAME

You can delete a VM only once it is shutdown with this command:

# virsh undefine $VM_NAME

VM Networking

Install the required packages

# apt install dnsmasq-base bridge-utils iptables

Libvirt provides a NATed bridged network named "default" that allows the host to communicate with the guests. This network is available only for the system domains (that is VMs created by root or using the qemu:///system connection URI). VMs using this network end up in and DHCP is provided to them via dnsmasq. This network is not automatically started. To start it use:

# virsh net-start default

Start it automatically:

# virsh net-autostart default

If you get an error that says that the "default" network does not exist, then you have to define it:

# virsh net-define /usr/share/libvirt/networks/default.xml

Setting Up Wake-On-LAN (WOL)

Install the required packages:

# apt install ethtool

Manually enable WOL on an interface:

# ethtool -s $IFACE_NAME wol g

This setting does not stick on reboot so you can create a systemd service to run the command on start:

# vim /etc/systemd/system/wol@.service
    Description=Wake-on-LAN for %i
    ExecStart=/usr/bin/ethtool -s %i wol g

Start and enable the service for your particular interface:

# systemctl start wol@$IFACE_NAME
# systemctl enable wol@$IFACE_NAME