My Debian 12 (bookworm) server set-up

I've been running Debian on my servers for years. It's dependable. I guess my server set-up is pretty common, consisting of Apache, PHP and MariaDB, but I figure it is still worth sharing. That said, some of the commands and configuration settings below are purposely generic, if you follow any of this, you should probably check the commands and their output before running them.

This post covers Debian 12 (bookworm) only.

Installation

I install Debian using the netinst image and I deselect all options in tasksel, apart from the SSH Server and Standard System Utilities. This provides for a very minimal installation and good starting point to set-up my server with packages of my own choosing.

Screenshot of tasksel showing SSH server and Standard System Utilities selections

Note, detailing how to install Debian is beyond the scope of this post, but if you need some guidance, please see the Debian Installation Guide.

Post Installation

It probably does not need to be mentioned, but post installation steps can be run on any Debian install. For example, I normally perform these steps on my desktop install of Debian to provide a working development environment.

Install packages

Once the installation is complete, I install the following packages. Most of the packages relate to Apache, PHP and MariaDB, but there are some utility packages included, as well as fish, my preferred shell.

sudo apt install git curl fish wget unzip bat apache2 apache2-bin apache2-data apache2-utils mariadb-client mariadb-server php php-fpm php8.2 php-common php-gd php-getid3 php-mysql php8.2-fpm php8.2-cli php8.2-common php8.2-gd php8.2-mysql php-ldap php8.2-redis php8.2-opcache php8.2-soap php8.2-readline php8.2-curl php8.2-xml php-imagick php8.2-intl php8.2-zip php8.2-mbstring ssl-cert imagemagick php-imagick redis-server php-curl ntpsec php-bcmath php-gmp php-mbstring php-curl php-mbstring php-sqlite3 sqlite3

Configure Apache

I issue the following commands to configure Apache and enable Apache modules:

sudo a2enmod proxy_fcgi setenvif && sudo a2enconf php8.2-fpm && sudo systemctl reload apache2 && sudo a2enmod rewrite && sudo a2enmod ssl && sudo a2enmod http2 && sudo a2enmod headers && sudo a2enmod rewrite && sudo a2enmod ssl && sudo systemctl restart apache2

Apache virtual host files can be found in the /etc/apache2/sites-available directory and have a .conf extension. Enabled virtual host files are linked to these files and can found in the /etc/apache2/sites-enabled directory. To enable a virtual host file, enter the following command, where virtualhostfile corresponds to the virtual host file located in /etc/apache2/sites-available without the .conf file extension.

sudo a2ensite virtualhostfile

You can disable virtual hosts with the following command:

sudo a2dissite virtualhostfile

You will need to reload Apache after any changes are made. This can be achieved with the following command:

sudo systemctl reload apache2

Example Apache Virtual Host file

I thought it might be handy to include an example virtual host file. The example below is from my own development environment, you will need to change the DocumentRoot and Directory path names to suit. The example is a virtual host file for use with CodeIgniter 4 and once it is set-up, the host should be reachable on the domain http://codeigniter.localhost.

<VirtualHost *:80>
    DocumentRoot /home/username/Projects/codeigniter/public
    ServerName codeigniter.localhost
    <Directory "/home/username/Projects/codeigniter/public">
        AllowOverride All
        Options MultiViews Indexes FollowSymLinks
        Require all granted
    </Directory>
    RewriteEngine on
</VirtualHost>

Configure PHP

I run the following commands to modify some PHP settings such file upload and execution time limits. You should adjust these to suit your needs:

sudo sed -i 's/memory_limit = 128M/memory_limit = 512M/g' /etc/php/8.2/fpm/php.ini && sudo sed -i 's/post_max_size = 8M/post_max_size = 128M/g' /etc/php/8.2/fpm/php.ini && sudo sed -i 's/max_file_uploads = 20/max_file_uploads = 30/g' /etc/php/8.2/fpm/php.ini && sudo sed -i 's/max_execution_time = 30/max_execution_time = 900/g' /etc/php/8.2/fpm/php.ini && sudo sed -i 's/max_input_time = 60/max_input_time = 3000/g' /etc/php/8.2/fpm/php.ini && sudo sed -i 's/upload_max_filesize = 2M/upload_max_filesize = 128M/g' /etc/php/8.2/fpm/php.ini && sudo systemctl restart php8.2-fpm

Install composer

I use composer to install and manage PHP applications and libraries. You can install composer with the following commands. Note, there is no hash check in these commands, use at your own risk. See official documentation for full installation instructions.

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
php -r "unlink('composer-setup.php');"
sudo mv composer.phar /usr/local/bin/composer

Configure MariaDB

I have found that MariaDB works pretty well out-of-the-box on Debian and is configured with sane default settings. That said, you may or may not want to edit some settings depending on how database dependant your applications are. The main configuration file is located at /etc/mysql/mariadb.conf.d/50-server.cnf. Some additional settings that are not included in that file and that you might want to take a look at include:

# InnoDB Settings
innodb_buffer_pool_size = 3G  # Allocates 3GB of memory for the InnoDB buffer pool to cache data and indexes, reducing disk I/O.
innodb_log_file_size = 512M  # Sets the size of each InnoDB log file, improving write performance and reducing log rotations.
innodb_flush_method = O_DIRECT  # Uses O_DIRECT to bypass filesystem cache, minimizing double buffering and improving performance.
innodb_io_capacity = 2000  # Sets the I/O capacity for InnoDB background tasks, suitable for fast storage like SSDs.

# Connection Settings
max_connections = 500  # Limits the maximum number of concurrent database connections.
thread_cache_size = 50  # Specifies the number of threads cached for reuse, reducing thread creation overhead.
thread_handling = pool-of-threads  # Enables thread pooling, which is more efficient for high-concurrency workloads.

# Cache and Buffer Settings
tmp_table_size = 64M  # Maximum size for in-memory temporary tables; larger tables will be written to disk.
max_heap_table_size = 64M  # Maximum size for user-created in-memory tables, working with tmp_table_size.
table_open_cache = 2000  # Number of open tables the server can cache to improve performance.
table_definition_cache = 2000  # Number of table definitions cached to reduce overhead when opening tables.
join_buffer_size = 4M  # Allocates memory for joins without indexes; larger size improves performance for complex joins.
sort_buffer_size = 4M  # Memory allocated for sorting operations; larger size enhances ORDER BY and GROUP BY performance.

# Logging Settings
slow_query_log = 1  # Enables logging of slow queries to help identify and optimize inefficient queries.
slow_query_log_file = /var/log/mysql/mariadb-slow.log  # Specifies the location of the slow query log file.
long_query_time = 1  # Logs queries that take longer than 1 second to execute.
log_queries_not_using_indexes = 1  # Logs queries that don’t use indexes, helping identify areas for optimization.

# Disable Query Cache
query_cache_type = 0  # Disables the query cache, which is often ineffective for workloads with frequent data changes.
query_cache_size = 0  # Sets the query cache size to 0, ensuring it is fully disabled and does not consume memory.

MariaDB will need restarting after any changes to the file, this can be achieved with the following command:

sudo systemctl restart mariadb

Create MariaDB user

To create a new admin user for MariaDB, start the MariaDB client with the following command:

sudo mariadb

Once opened, enter the following statements to create a new user with all privileges on localhost. Note, change the username and password to suit.

CREATE USER 'username'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'username'@'localhost' WITH GRANT OPTION;

Once the statements have been run, type exit to quit the client.

Create MariaDB user configuration file

If you are going to be running any automated DB tasks such as backups, it might be handy to create a personal MariaDB configuration file to store your MariaBD username and password. This will allow you to write scripts that perform DB dumps without having to include your password in the script. The configuration file can be edited with the following command:

nano ~/.my.cnf

Include the following content in the file, changing the username and password to suit:

[mysql]
user = myusername
password = mypassword

[mysqldump]
user = myusername
password = mypassword

Once the file has been created, you should modify the permissions so only you can read/write. This is done with the following command:

chmod 600 ~/.my.cnf

Install Node.js

If required, I install Node.js via the NodeSource Debian repository using the following shell script. At the time of writing I am using Node.js version 22. You can change which version of Node.js is installed by editing the NODE_MAJOR variable.

#!/usr/bin/bash
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg

NODE_MAJOR=22
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list

sudo apt-get update
sudo apt-get install nodejs -y

exit

Configure UFW Firewall

If the server is public facing, I set-up a firewall using UFW. UFW can be installed with the following command:

sudo apt install ufw

Once installed, the following commands will configure the firewall to allow web traffic and SSH connections:

sudo ufw allow OpenSSH
sudo ufw allow WWW
sudo ufw allow "WWW Secure"
sudo ufw enable

You can check the status of the firewall with the following command:

sudo ufw status verbose

If configured correctly, the output of the above command should look similar to below:

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp (OpenSSH)           ALLOW IN    Anywhere                  
80/tcp (WWW)               ALLOW IN    Anywhere                  
443/tcp (WWW Secure)       ALLOW IN    Anywhere                  
22/tcp (OpenSSH (v6))      ALLOW IN    Anywhere (v6)             
80/tcp (WWW (v6))          ALLOW IN    Anywhere (v6)             
443/tcp (WWW Secure (v6))  ALLOW IN    Anywhere (v6)

Set-up fish shell

This is a personal preference, but I use fish as my default shell, preferring it over the default Bash shell. I set fish as the default shell with the following command:

chsh -s /usr/bin/fish

Note, the above command will prompt you for your user password. Also, you will need to logout before it will become the default shell. Meanwhile, you can use fish by typing fish at the Bash prompt.

Oh My Fish and Pure theme

I use the Oh My Fish framework to enhance my fish experience. It can be installed with the following command:

curl https://raw.githubusercontent.com/oh-my-fish/oh-my-fish/master/bin/install | fish

Once installed, I install the Pure theme with the following command:

omf install pure

Finishing touches

Finishing touches might include things such as importing or creating SSH keys etc. Other than that, any other changes would be specific to the applications being hosted or developed on the server.

apache composer debian fish linux mariadb node php

Comments

New Comment

If you have a comment you'd like to share, feel free to leave it below. I moderate all comments before they are published. Markdown is enabled. See syntax for help.


I'll never share your email with anyone else.

Blog

I mostly try to write non-opinionated posts about web development and Linux - reminders about how to complete tasks and get stuff done etc.

For short-form posts, see my status page.

Subscribe

Click the link below to subscribe to my Blog feed in your favourite news reader client:

RSS feed

Philip Newborough and a donkey enjoying a beer.

About

My name is and I’m a full stack web developer living and working in Lincoln, England. This website (philipnewborough.co.uk) serves as my personal homepage. When I’m not working with tech, I love to ride bicycles with my wife and friends.

An IndieWeb Webring 🕸💍