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.
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.
Comments