Essential Initial Setup for a Secure Debian Server

| The first secure boot, somewhere between tea and dusk. |
Table of Contents 📑
- Changelog
- Step 1: Initial Login, Update and Hostname
- Step 2: Create a Dedicated Admin User
- Step 3: Harden SSH Access
- Step 4: Configure a Firewall with UFW
- Step 5: Prevent Intrusion with Fail2Ban
- Step 6: Automate Security Updates
- Step 7: Final System Housekeeping
- Step 8: Basic Monitoring and Logging
- Step 9: Backup Strategy
- Step 10 (Optional): SSH via WireGuard
- Conclusion
- Sources
Welcome to the essential security guide for any new Debian server. A fresh installation is a blank slate, and taking the right steps immediately after setup is crucial for protecting your server from threats. This guide provides a logical, step-by-step process to establish a strong security baseline.
We will cover user management, hardening SSH access, configuring a firewall, setting up automatic intrusion prevention, and enabling automatic security updates.
| ℹ️ FOLLOW THE ORDER |
These steps are designed to be followed sequentially. For example, we will set up SSH key authentication before disabling password logins to ensure you don’t lock yourself out. |
Changelog
| Date | Change |
|---|---|
| 2026-10-12 | Check host services and preserved ProFTPD modules after major upgrades. |
| 2026-10-11 | Clarify supported upgrades, Debian 13 compatibility and Docker socket verification. |
| 2026-10-09 | Fixed SSH staging and automation risks, clarified update policy, and strengthened backup and VPN recovery guidance. |
| 2026-07-01 | Added rsyslog/UFW logging notes and clarified Docker firewall caveats. |
| 2026-03-03 | Comprehensive update: hardened SSH config, added backup strategy, monitoring, and WireGuard. |
| 2025-07-10 | Initial version of the comprehensive Debian hardening guide. |
Step 1: Initial Login, Update and Hostname
Use a supported Debian release and confirm the provider’s recovery-console access before changing SSH or networking. Check the server’s host-key fingerprint through that console before accepting the first SSH connection; do not bypass host-key checking. The provider may supply a password or an initial authorized public key.
For a new installation, choose the current supported stable release. Debian 13 is the current stable series. Debian 11 regular LTS ended on 31 August 2026; continued coverage requires a separate extended-support arrangement. Package updates alone do not move an installation to a supported release. For existing systems, follow the release notes one major release at a time, with a tested backup and recovery console, and verify services after each reboot. For example, upgrade Debian 11 to 12 before proceeding to 13. Review third-party repositories, package holds and configuration changes at each step; do not globally replace release names or skip directly across releases. Before upgrading a remote Debian 12 host to 13, install the latest Debian 12 OpenSSH updates; the Debian 13 release notes document a reconnect issue fixed in 1:9.2p1-2+deb12u7 or later. Debian 13 also changes /tmp to tmpfs by default after reboot and no longer loads /etc/sysctl.conf through systemd-sysctl. Check whether services depend on disk-backed temporary storage, and move required local kernel settings into /etc/sysctl.d/*.conf. Preserve the existing values and test the resulting storage and network behavior after reboot. Debian 13 upgrade issues. Debian 11 lifecycle, Debian 13 lifecycle, Debian release upgrade preparation.
Verify host services after each major upgrade
Record the expected services and existing failures before upgrading. After each reboot, check package consistency and compare the host-service state with that baseline; healthy containers alone do not cover host services:
sudo dpkg --audit
systemctl --failed
sudo journalctl -b -u your-service.serviceDiagnose new failures before continuing to another release. Preserve intentional service stops and existing access restrictions; clearing a failed state does not repair its cause.
For an existing ProFTPD installation, Debian 13 can expose missing module loads in a preserved older /etc/proftpd/modules.conf. Compare it with the package-provided replacement and installed modules. The transfer and listing modules must be loaded dynamically; an existing DelayEngine directive also requires mod_delay. Retain the original configuration, authentication policy and data, then merge the required lines rather than replacing the entire configuration:
LoadModule mod_xfer.c
LoadModule mod_ls.c
# Required when the retained configuration uses DelayEngine:
LoadModule mod_delay.cVerify the configuration with sudo proftpd --configtest before restoring the expected service. Check its listener and legitimate client workflow afterwards. Do not remove ShowSymlinks or disable the delay protection merely to hide a missing-module error. Debian ProFTPD maintainer’s explanation.
These steps target a fresh server. On an existing production system, inventory its services and configuration, establish backups and schedule disruptive updates first.
The very first action should always be to update the package list and upgrade all installed packages. For a fresh server, we use full-upgrade to ensure even kernel updates and new dependencies are handled.
# Log in as root
ssh root@your_server_ip
# Update package lists
apt update
# Upgrade installed packages and resolve required dependency changes
apt full-upgrade
# Planned reboot after the update; verify the running system afterwards
rebootAfter the reboot, log back in. It’s best practice to set the hostname immediately so all subsequent logs reflect the correct name.
# Set your server's hostname
hostnamectl set-hostname my-awesome-serverKeep /etc/hosts consistent with the chosen hostname while preserving provider-managed entries.
Check the virtualization model and the actual running kernel:
systemd-detect-virt
uname -r
cat /etc/os-releaseA full VM normally boots its own kernel. A container VPS such as OpenVZ or LXC uses a host kernel controlled by the provider: installing a Debian kernel package or restarting the container does not replace it. Clarify kernel security maintenance, support duration and supported guest features with the provider. Userspace updates remain a separate responsibility. OpenVZ container model.
Step 2: Create a Dedicated Admin User
Operating directly as the root user is risky. We will create a new user account with administrative privileges via the sudo command. This improves security and provides better auditing.
Replace adminuser with a username of your choice.
# Install prerequisites if the image does not include them
apt install sudo openssh-server
# Create a new user
adduser adminuser
# You will be prompted to set a password and fill in user information.
# Add the new user to the 'sudo' group
usermod -aG sudo adminuser
# Keep the original root session open until a separate admin login
# and password-authenticated sudo have both been verified.| ℹ️ SUDO PASSWORD SECURITY |
By default, |
Use the new user for normal administration once its key login and sudo have been tested. Do not disable the recovery root login before that test succeeds.
Step 3: Harden SSH Access
Securing SSH is the single most effective thing you can do to protect your server. While we will change the default port to reduce log noise from automated scanners, the real security comes from key-only authentication and the hardening settings below.
3.1. Set Up SSH Key Authentication
SSH keys are far more secure than passwords. A key pair consists of a private key (which you keep on your local computer) and a public key (which you place on the server).
On your local computer (not the server): If you don’t have an SSH key pair yet, generate one. The ed25519 algorithm is modern and highly secure.
# This command is run on your local machine
ssh-keygen -t ed25519 -C "your_email@example.com"Press Enter to accept the default file location and set an optional (but recommended) passphrase for your key.
Copy your public key to the server: The ssh-copy-id command is the easiest way to do this. It will automatically add your public key to the correct file on the server.
# Replace the public-key path, username and server IP as needed
ssh-copy-id -i ~/.ssh/id_ed25519.pub adminuser@your_server_ipOpen a separate admin connection, confirm the expected key works, and test sudo -v followed by sudo id -u (expected output: 0). Keep the original session open. If password SSH is already disabled, use the existing authorized administrator session to install the selected public key instead; never copy a private key. The user’s .ssh directory should be owned by that user with mode 0700, and authorized_keys with mode 0600.
3.2. Stage the SSH Port Change
Keep the current session open and the recovery console available. Determine the current SSH port and check provider firewall rules. The following example assumes the existing port is 22 and the new port is 8496; adjust both values when needed.
sudo apt install ufw
sudo ufw allow 22/tcp comment 'temporary SSH bootstrap'
sudo ufw limit 8496/tcp comment 'administrative SSH'
sudo cp -a /etc/ssh "/root/ssh-before-hardening-$(date +%Y%m%dT%H%M%S)"Allow the new port in any provider firewall before reloading SSH. Preserve both ports until a fresh external login succeeds.
OpenSSH generally uses the first value read for a setting. An early Include can override a later line in the main file, while options such as Port and AllowUsers can accumulate. Inspect the main file, included snippets and Match blocks; do not assume a late 99-hardening.conf wins. Confirm the main file includes sshd_config.d/*.conf before other relevant directives. OpenSSH configuration semantics.
Before applying this policy to an existing host, inventory every legitimate SSH/SFTP account, backup and deployment key, jump host and tunnel. AllowUsers excludes accounts outside its list; disabling root SSH or forwarding can break automation. Preserve required access with reviewed user-specific policy and test each affected workflow. A hypervisor or managed NAS may require its own configuration mechanism rather than this fresh-server example.
Clients offering many agent keys can exceed MaxAuthTries 3: select the intended key with -i and IdentitiesOnly=yes. UFW connection limits can also affect parallel automation; test its connection pattern before adopting the limit.
On a fresh installation, create a reviewed early snippet; if the file already exists, inspect and merge it instead of overwriting it:
sudo tee /etc/ssh/sshd_config.d/00-local-hardening.conf > /dev/null <<'EOF'
Port 22
Port 8496
PermitRootLogin prohibit-password
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3
LoginGraceTime 30
AllowUsers root adminuser
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
PermitTunnel no
EOFThis first stage permits root key access as a recovery path only if a root key was already installed and tested. The existing root session and provider console remain separate recovery paths. Password and keyboard-interactive SSH are disabled. Do not restrict accepted key algorithms blindly: modern RSA keys can use RSA-SHA2 signatures, and an Ed25519-only list would reject them. Test every required administrator key before adopting a narrower policy; do not re-enable obsolete SHA-1 signatures for convenience. OpenSSH accepted-key algorithms.
3.3. Verify Effective Settings and Test a Fresh Login
sudo /usr/sbin/sshd -t
# Replace host/client placeholders with the actual connection context.
sudo /usr/sbin/sshd -T -C user=adminuser,host=client.example.com,addr=198.51.100.10,laddr=203.0.113.10,lport=8496
sudo /usr/sbin/sshd -T -C user=root,host=client.example.com,addr=198.51.100.10,laddr=203.0.113.10,lport=8496
sudo systemctl is-active ssh.socketThe documentation addresses above are placeholders. In -C, host is the resolved client hostname, addr the client address, and laddr/lport the server listener. Test each relevant connection context, including the temporary port during staging. OpenSSH connection-test parameters. Inspect the effective ports, authentication methods and allowed users, including applicable Match blocks. sshd -t checks validity; it does not establish that the intended policy is effective.
If ssh.socket is active, its listening addresses also need a reviewed update; changing the daemon configuration alone is insufficient. For the ordinary ssh.service setup with socket activation inactive, reload rather than restarting the daemon:
sudo systemctl reload ssh.service
sudo ss -lntpOpen a new connection from the client:
ssh -p 8496 adminuser@your_server_ip
sudo -v
sudo id -uVerify key authentication and working sudo before closing the original session. Once both pass, disable root SSH and remove the bootstrap port from the reviewed snippet, then resolve any remaining conflicting directives in other files:
# Edit the actual username and ports you selected; preserve required automation.
sudoedit /etc/ssh/sshd_config.d/00-local-hardening.conf
# Final fresh-server policy: PermitRootLogin no, AllowUsers adminuser,
# and only the new Port. Verify these lines after editing.
sudo grep -nE '^(Port|PermitRootLogin|AllowUsers)' /etc/ssh/sshd_config.d/00-local-hardening.conf
sudo /usr/sbin/sshd -t
sudo /usr/sbin/sshd -T -C user=adminuser,host=client.example.com,addr=198.51.100.10,laddr=203.0.113.10,lport=8496
sudo /usr/sbin/sshd -T -C user=root,host=client.example.com,addr=198.51.100.10,laddr=203.0.113.10,lport=8496
sudo systemctl reload ssh.serviceConfirm only the intended port remains, PermitRootLogin no is effective, and a fresh admin connection still works. Leave the temporary UFW rule until the firewall is enabled and verified in Step 4; then remove it and the corresponding provider rule. Preserve the provider’s console recovery mechanism; disabling root SSH does not require locking the root account.
Step 4: Configure a Firewall with UFW
A firewall is essential for controlling network traffic. We will use UFW (Uncomplicated Firewall) because it is user-friendly and effective.
# Install UFW
sudo apt install ufwNext, we will set up some basic rules. By default, we will deny all incoming traffic and allow all outgoing traffic. Then we will explicitly allow traffic for the services we need.
# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow standard web traffic
sudo ufw allow 80/tcp # Only if this server will provide HTTP
sudo ufw allow 443/tcp # Only if this server will provide HTTPS
# IMPORTANT: Allow your new SSH port
# We use 'limit' to help protect against brute-force attacks
# The SSH limit was installed in Step 3.2; verify that rule remains present.
sudo ufw status verboseNow, enable the firewall. It will ask for confirmation to proceed.
sudo ufw enable
# After the new SSH login is verified, remove its temporary old-port rule.
sudo ufw delete allow 22/tcp
# Check the status of the firewall at any time
sudo ufw status verbose| 💡 CLOUD FIREWALL |
Cloud providers like Hetzner, DigitalOcean, or AWS offer cloud-level firewalls in their web panel. These filter traffic before it reaches your server. If available, mirror your UFW rules there as an additional layer. |
| ⚠️ DOCKER AND UFW |
Docker publishes ports by adding its own iptables/nftables rules. UFW is still useful, but it should not be treated as the only network boundary for a Docker host. Prefer publishing only the reverse proxy ports ( |
Step 5: Prevent Intrusion with Fail2Ban
Key-only SSH removes password-guessing as an authentication path, but does not prevent stolen-key use, implementation vulnerabilities or resource-exhaustion attacks. So why Fail2Ban? Because your server will likely run more than SSH. Other services can be protected with separately configured and tested jails; installing Fail2Ban does not automatically protect every deployed application. It is a general-purpose intrusion prevention tool. For container-published ports, a default host INPUT-chain ban may not affect Docker-forwarded traffic. Use a firewall action appropriate to the actual Docker backend and path, and prove enforcement against the real service; do not assume that adding a web jail protects a container. Docker firewall behavior.
# Install Fail2Ban
sudo apt install fail2banUse a small jail override rather than copying the entire packaged jail.conf. On a systemd host, the journal backend avoids depending on a legacy /var/log/auth.log file:
sudo apt install python3-systemd
sudo tee /etc/fail2ban/jail.d/sshd.local > /dev/null <<'EOF'
[sshd]
enabled = true
backend = systemd
port = 8496
maxretry = 3
bantime = 1h
findtime = 10m
EOF
sudo fail2ban-client -t
sudo systemctl enable --now fail2ban
sudo systemctl restart fail2banAllow the daemon to finish starting, then inspect the actual jail:
sudo fail2ban-client status sshdConfirm the journal contains SSH events and that the configured port and firewall action match the deployment. A running Fail2Ban service does not prove every intended jail is active. Debian Fail2Ban guidance.
| 💡 VERSATILE PROTECTION |
Fail2Ban is incredibly powerful. You can configure it to protect many other services, such as web servers (Nginx, Apache) or mail servers, by creating custom jails. |
Step 6: Automate Security Updates
Even with a hardened server, new vulnerabilities are discovered all the time. It is vital to install security updates promptly. The unattended-upgrades package can do this for you automatically.
# Install the package
sudo apt install unattended-upgrades apt-listchangesNow, run the configuration wizard to enable it.
# This will open a simple text-based interface
sudo dpkg-reconfigure -plow unattended-upgradesSelect “Yes” to enable automatic updates. This will create a configuration file that tells the system to automatically install packages from Debian’s security repository.
Review the enabled update origins and monitoring. The configuration wizard enables periodic upgrades; it is not proof that a successful unattended run has occurred. Use the package logs and timer state to verify actual execution. Docker images, third-party repositories and a provider-managed kernel require their own update plans. Package security updates can also restart daemons without rebooting the host: review Docker/runtime and other service restarts, their monitoring and maintenance policy. Docker live-restore can reduce disruption in supported cases, but does not guarantee uninterrupted service for every upgrade. After a daemon upgrade, verify that containers using a bind-mounted Docker socket can still query the current daemon and discover newly recreated containers. If a socket was replaced, renew the affected client container’s mount through a controlled restart; existing HTTP routes alone can hide a stale provider connection. Docker live-restore limits. Debian unattended upgrades.
For a service host, keep automatic reboots disabled until an explicit downtime and recovery policy exists. Put local policy in a separate snippet:
sudo tee /etc/apt/apt.conf.d/52-local-reboot-policy > /dev/null <<'EOF'
Unattended-Upgrade::Automatic-Reboot "false";
EOF
sudo systemctl enable --now apt-daily.timer apt-daily-upgrade.timer
sudo apt-config dump
sudo systemctl list-timers apt-daily.timer apt-daily-upgrade.timerArrange a maintenance-window restart when needed and verify services afterwards. If automatic restarts are acceptable for a particular host, configure and test that policy deliberately. Email reports require a working mail transport and a tested recipient; merely setting Unattended-Upgrade::Mail does not establish delivery.
Step 7: Final System Housekeeping
A few final touches will make your server easier to manage.
1. Set the Timezone: Correct log timestamps are crucial for troubleshooting.
# Set your timezone, e.g., for Berlin
sudo timedatectl set-timezone Europe/Berlin2. Install Useful Tools: These small utilities are incredibly helpful for administration.
sudo apt install htop ncdu curl githtop: An interactive process viewer.ncdu: A disk usage analyzer to easily find large files.curl: A tool for transferring data with URLs.git: Version control system, often needed to download software.
Step 8: Basic Monitoring and Logging
A secure server is one where you notice when something goes wrong.
8.1. Persistent Journal Logging
On some Debian installations, journal logs are lost upon reboot. This makes them persistent:
sudo mkdir -p /etc/systemd/journald.conf.d
sudo tee /etc/systemd/journald.conf.d/60-local-retention.conf > /dev/null <<'EOF'
[Journal]
Storage=persistent
SystemMaxUse=512M
SystemKeepFree=1G
MaxRetentionSec=30day
EOF
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
sudo journalctl --disk-usageChoose retention and free-space reserves for the actual workload. Verify global logrotate configuration and avoid duplicate rules for the same file. Docker-managed logs need logging-driver limits, not external truncation. systemd journal retention.
8.2. UFW Logs with rsyslog (Optional)
If you want UFW messages in /var/log/ufw.log instead of relying only on the journal, install rsyslog before adding rsyslog snippets.
sudo apt install rsyslogFirst inspect packaged rules: UFW and rsyslog may already provide the desired routing and log rotation. Add a filter only if none exists for this purpose. Example:
sudo tee /etc/rsyslog.d/19-local-ufw.conf > /dev/null << 'EOF'
:msg,contains,"[UFW " /var/log/ufw.log
& stop
EOFValidate and restart:
sudo rsyslogd -N1
sudo systemctl restart rsyslogWithout the rsyslog package, this configuration file will simply never be processed.
8.3. Logwatch (Optional)
Logwatch provides a daily summary of SSH attempts, disk usage, and service errors.
sudo apt install logwatch
sudo logwatch --detail Med --range yesterday --output stdout8.4. Monitoring Disk Space
Full disks are a common cause of service failures.
df -h
sudo ncdu -x /Step 9: Backup Strategy
Security requires prevention, detection and recovery. Before production, verify failed-unit monitoring, storage alerts, a recent backup and an isolated restore; a configured timer alone is not sufficient.
Core Principles
- Provider Snapshots: Use them where available, with application-consistency requirements understood.
- Application-Level Backups: Use tools like
borgbackuporrestic. - 3-2-1 Rule: 3 copies, 2 different media, 1 offsite.
- Test Restores: A backup is only as good as its last successful restore.
Example with BorgBackup 1.x:
Use an independent backup destination; a repository on the same disk is not offsite protection. Export and securely store the repository key and passphrase outside the server. The following is an example for a reviewed quiescent file tree, not a live database backup:
sudo apt install borgbackup
sudo borg init --encryption=repokey /path/to/backup/repo
sudo borg create '/path/to/backup/repo::{hostname}-{now}' /path/to/quiescent-data
sudo borg list /path/to/backup/repoRun backup and restore commands consistently as the chosen backup account (root in this example), and use a protected passphrase source for unattended operation. Use commands matching the installed Borg major version. Live databases need application-aware dumps or a supported consistent snapshot procedure. Copying live database directories or all of /var/lib/docker is not a reliable substitute. Include persistent application data, required configuration and recovery secrets with appropriate protection; container images and caches are separate choices. Borg quick start.
Listing an archive proves visibility, not recoverability. Restore a representative archive into an isolated destination, verify contents and ownership, and test application/database recovery. Document a recent successful backup run and a restore result. Provider snapshots are useful where available but do not replace independent, consistent backups.
Step 10 (Optional): SSH via WireGuard
For an additional access boundary, make SSH reachable only through a WireGuard VPN tunnel. This removes the public SSH listener while leaving the VPN endpoint to maintain and monitor. Test the tunnel and preserve independent recovery access before restricting SSH.
10.1. Setup WireGuard Server
sudo apt install wireguard
# Create directory with restricted permissions first
sudo install -d -m 700 /etc/wireguard
sudo sh -c 'umask 077; wg genkey | tee /etc/wireguard/server_private.key | wg pubkey > /etc/wireguard/server_public.key'The pipeline forwards the private key to wg pubkey without printing it as final terminal output. Both key files still need protected storage. Do not paste a private key into logs or messages. Confirm /etc/wireguard exists with restricted access and run key generation in a privileged shell.
On a container VPS, first verify that the provider supports the required WireGuard kernel functionality or an explicitly chosen userspace implementation. Installing the package alone does not establish kernel support. WireGuard installation.
10.2. Server Configuration
Create /etc/wireguard/wg0.conf:
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <contents of server_private.key>
[Peer]
PublicKey = <client_public_key>
AllowedIPs = 10.0.0.2/32Protect the configuration containing the private key, then enable the service:
sudo chmod 600 /etc/wireguard/wg0.conf
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg010.3. Configure the Client
Configure a WireGuard client with its own key pair:
# /etc/wireguard/wg0.conf on the client
[Interface]
Address = 10.0.0.2/24
PrivateKey = <client_private_key>
[Peer]
PublicKey = <server_public_key>
Endpoint = your_server_ip:51820
AllowedIPs = 10.0.0.1/3210.4. Open the VPN Path and Test It First
Keep public SSH and the original session available while opening the VPN path. Allow UDP 51820 in the provider firewall too:
sudo ufw allow 51820/udp
sudo ufw allow in on wg0 to any port 8496 proto tcpActivate the client tunnel and open a separate SSH session to the VPN address:
ssh -p 8496 adminuser@10.0.0.1Confirm authentication and sudo work over this connection before restricting the listener or deleting public SSH rules.
10.5. Restrict SSH to the VPN
On the ordinary ssh.service setup, add the listener address to the reviewed SSH configuration:
ListenAddress 10.0.0.1Inspect all included files and remove other explicit broad ListenAddress entries: they can accumulate. For socket-activated SSH, review the socket’s listening addresses separately. Ensure the VPN interface is ready before the SSH service starts; otherwise a reboot can leave SSH unable to bind. A service drop-in created with sudo systemctl edit ssh.service can express the order:
[Unit]
Wants=wg-quick@wg0.service
After=wg-quick@wg0.service
[Service]
Restart=on-failure
RestartPreventExitStatus=
RestartSec=10sOrdering alone does not guarantee a working tunnel. This drop-in also allows retries after a fatal bind error; inspect the installed SSH unit and start-rate limits, and test recovery from delayed or failed VPN startup on a staging host. It does not replace the independent console.
systemd unit dependencies and ordering.
sudo systemctl daemon-reload
sudo /usr/sbin/sshd -t
sudo /usr/sbin/sshd -T
sudo systemctl reload ssh.service
sudo ss -lntpVerify the only SSH listener is the VPN address, then test another fresh VPN login. Only afterwards remove the public SSH limit rule from UFW and the corresponding provider-firewall rule:
sudo ufw delete limit 8496/tcpBefore declaring this complete, perform a planned reboot and verify the VPN, SSH and sudo again. Keep the provider console available in case the VPN fails. WireGuard quick-start guidance.
Conclusion
Congratulations! You have successfully performed the essential first steps to harden your new Debian server. By creating a sudo user, securing SSH with key authentication, and setting up a firewall and intrusion prevention system, you have built a solid foundation for any application you wish to deploy.
Production readiness still depends on tested application configuration, monitoring, maintenance and recovery. Hardening reduces exposure to common automated attacks; verified independent backups and working restores address data recovery after hardware failure.
| ℹ️ WHAT'S NEXT? |
With this secure base, you are now ready to install your applications, such as a web server (Nginx/Apache), a database, or a Docker environment. |
Sources
- Debian ProFTPD maintainer: required dynamic module loads after upgrade
- Debian 13 release upgrade issues
- OpenSSH connection-context test
- Docker live-restore limits
- OpenSSH configuration, effective policy and accepted algorithms
- Debian security manual
- Debian Fail2Ban guidance
- Debian unattended upgrades
- systemd journal retention
- Docker firewall behavior
- OpenVZ container model
- Borg backup and restore quick start
- WireGuard installation and platform support





