VPS Linux security checklist before putting services into production: hardening, SSH, backups and monitoring
By Kevin Marcondes · Published on · Updated on · 9 min read
Before go‑live, use this VPS Linux security checklist to harden the host, secure SSH access, enable TLS, configure firewalls and alerts, and validate backups. Follow these practical steps to reduce the attack surface, protect access, and ensure faster recovery.
How to use this checklist practically
Treat each section as a mandatory step before go‑live. Mark items as completed and record evidence: executed commands, changed files and screenshots from monitoring dashboards.
Apply the checklist at three moments: initial VPS setup, before every significant release and after incidents. Reassess permissions, open ports and update policies regularly.
Integrate with your change control: version configuration files, use reviewed PRs and approvals. Store evidence in an internal repository for audits.
If your team uses runbooks, include this checklist. It complements security policies and does not replace them.
- Assign an owner for each step.
- Record the date and verification result.
- Automate where possible (scripts and IaC).
Accounts, privileges and secure access
Separate administrative accounts from application accounts. Disable direct root login and use sudo only when necessary. Use SSH key authentication; never allow password authentication in production.
Restrict who can access via SSH and consider second factor (TOTP/U2F) and/or a bastion host for mediated access. See the guide on Set up an SSH Bastion on Ubuntu 22.04 for 2FA and bastion patterns: Set up an SSH Bastion on Ubuntu 22.04.
Rotate keys regularly and remove access for former employees. For services, prefer short-lived tokens and keep secrets out of code.
- Dedicated administrative account (e.g., deploy).
- No remote root login.
- Sudo with audit (sudo -l and logs).
- Strong, rotated SSH keys.
- Access limited by known IPs or VPN.
# criar usuário administrativo e configurar SSH por chave
adduser deploy
usermod -aG sudo deploy
mkdir -p ~deploy/.ssh && chmod 700 ~deploy/.ssh
# cole sua chave pública no arquivo abaixo
nano ~deploy/.ssh/authorized_keys
chmod 600 ~deploy/.ssh/authorized_keys
chown -R deploy:deploy ~deploy/.ssh
# desabilitar login root por SSH e reiniciar serviço
sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
systemctl reload sshd || service ssh restart
System updates and minimal services
Keep the system up to date and enable automatic security updates. Schedule reboots after kernel upgrades. Remove packages and services you don't need to reduce the attack surface.
Review services enabled at boot and disable those not required. Run periodic scans to detect outdated exposed packages.
Consider removing build tools on production hosts, uninstalling unused servers and blocking unnecessary kernel modules when appropriate.
- Updates applied and pending issues resolved.
- Automatic security updates enabled.
- Unnecessary services removed.
- Reboot scheduled after kernel updates.
# atualizar sistema e configurar atualizações automáticas (Debian/Ubuntu)
apt update && apt full-upgrade -y
apt install -y unattended-upgrades
# assistente para ativar atualizações automáticas
DPKG_FRONTEND=noninteractive dpkg-reconfigure --priority=low unattended-upgrades
# listar serviços habilitados para revisar
systemctl list-unit-files --type=service --state=enabled
Firewall and network hardening
Apply a default deny incoming / allow outgoing policy, opening only the ports you need. Rate-limit SSH connection attempts. Harden network parameters with sysctl to mitigate scans and spoofing.
For IPv6, ensure equivalent rules to IPv4. Perform an external scan (nmap) from another machine to verify only required services are exposed.
For a step‑by‑step UFW guide, see How to Set Up a Firewall on Ubuntu with UFW: How to Set Up a Firewall on Ubuntu with UFW.
- Default policy: deny incoming, allow outgoing.
- Minimal open ports (80/443 and 22 only when necessary).
- Rate-limit SSH (UFW limit).
- sysctl protections for IP spoofing and SYN cookies.
- Consistent IPv6 rules (if enabled).
| Port/Service | Recommended state | How to check |
|---|---|---|
| 22 / SSH | Open, limited and/or restricted by IP | ufw status | grep 22 |
| 80 / HTTP | Open only if there is a web app | ufw status | grep 80 |
| 443 / HTTPS | Open for TLS | ufw status | grep 443 |
| 9100 / Node Exporter | Restricted by IP or closed | ufw status | grep 9100 |
| 5432 / PostgreSQL | Closed (internal only) | ss -tulpn | grep 5432 |
# política de firewall com UFW
ufw default deny incoming
ufw default allow outgoing
ufw allow 80,443/tcp
ufw allow 22/tcp && ufw limit 22/tcp
ufw enable
ufw status verbose
Hardened SSH and, if possible, a bastion
Harden sshd_config for key-only auth, a low MaxAuthTries value and short keepalives. Don’t rely on changing port 22 as the only defense; prioritize strong authentication and IP/VPN restrictions.
Centralize team and third‑party access through a bastion with logging and 2FA. See the step‑by‑step in Set up an SSH Bastion on Ubuntu 22.04: Set up an SSH Bastion on Ubuntu 22.04.
Use fail2ban to block malicious attempts and set AllowUsers/AllowGroups explicitly. Review auth logs regularly for brute force signs.
- Key-only authentication (no passwords).
- PermitRootLogin no.
- Low MaxAuthTries (e.g., 3).
- Explicit AllowUsers list.
- Keepalive to disconnect idle sessions.
# /etc/ssh/sshd_config (trecho recomendado)
Port 22
Protocol 2
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
# restrinja usuários autorizados
AllowUsers deploy
UsePAM yes
# ative ChallengeResponseAuthentication apenas se usar 2FA via PAM
# ChallengeResponseAuthentication yes
TLS, HTTPS and application security
Use HTTPS for all public traffic. Automate certificate issuance and renewal with ACME, and enforce HSTS and 80→443 redirects. Configure reverse proxies with minimal, secure headers.
Evaluate supported protocols and ciphers, removing obsolete versions. Enable OCSP stapling and ALPN when applicable. Use caution with HSTS for subdomains if some services lack TLS.
For a practical guide on issuing certificates, see How to Get Free SSL with Let's Encrypt: How to Get Free SSL with Let's Encrypt.
- Valid certificates with automated renewal.
- HTTP to HTTPS redirect.
- HSTS enabled when appropriate.
- Modern TLS without obsolete protocols.
# emitir certificado usando o modo standalone (pare o serviço HTTP temporariamente)
apt update && apt install -y certbot
certbot certonly --standalone \
-d example.com -d www.example.com \
--agree-tos -m [email protected] --no-eff-email
# teste de renovação
certbot renew --dry-run
Monitoring, logs and actionable alerts
Collect host metrics (CPU, memory, disk, network), availability and application errors. Configure alerts with clear thresholds and on‑call owners. Retain logs with rotation and centralization when possible.
Add external uptime checks from outside the VPS. Alerts for certificate expiry, backup failures and disk usage prevent incidents. Document who responds to alerts and the response playbook.
To visualize metrics, combine Prometheus and Grafana. See the Docker Compose guide: Guide: Prometheus & Grafana with Docker Compose: Guide: Prometheus & Grafana with Docker Compose.
- Host metrics (Node Exporter).
- Dashboards for critical services.
- Alerts for full disk, OOM and latency spikes.
- Logs with rotation and defined retention.
- fail2ban to block malicious IPs.
# instalar métricas básicas do host (Debian/Ubuntu)
apt update && apt install -y prometheus-node-exporter
systemctl enable --now prometheus-node-exporter
# restrinja a porta 9100 ao IP do Prometheus (exemplo)
ufw allow from 203.0.113.10 to any port 9100 proto tcp
Tested backups and recovery plan
Define what to back up: databases, application files, configs and secrets. Do automated backups external to the VPS, with retention and encryption. The critical part is testing restores: simulate recovery regularly.
Include integrity checks, failure alerts and disk space monitoring. Document who triggers the plan and target recovery times for each service. Be cautious storing keys and passwords in scripts.
Set realistic RPO/RTOs, validate restores in an isolated environment and keep passphrases outside the VPS. If using provider snapshots, combine them with consistent database dumps.
- Automated off‑site encrypted backups.
- Regular restoration tests.
- Layered retention (daily/weekly/monthly).
- Documented recovery plan.
- Restricted access to backup keys.
#!/usr/bin/env bash
# backup básico (exemplo). Ajuste caminhos e credenciais.
set -euo pipefail
TS=$(date +%F-%H%M)
HOST=$(hostname -s)
BACKUP_DIR=/var/backups
REMOTE="[email protected]:/backups/${HOST}/"
mkdir -p "$BACKUP_DIR/db" "$BACKUP_DIR/files"
# dumps (ajuste usuários e senhas via .pgpass/.my.cnf ou variáveis seguras)
pg_dumpall | gzip > "$BACKUP_DIR/db/postgres-$TS.sql.gz" || true
mysqldump --all-databases --single-transaction | gzip > "$BACKUP_DIR/db/mysql-$TS.sql.gz" || true
# arquivos críticos
tar czf "$BACKUP_DIR/files/etc-$TS.tgz" /etc
tar czf "$BACKUP_DIR/files/app-$TS.tgz" /var/www || true
# sincronizar para armazenamento externo
rsync -az --delete "$BACKUP_DIR/" "$REMOTE"
# retenção local de 7 dias
find "$BACKUP_DIR" -type f -mtime +7 -delete
echo "Backup concluído em $TS"
Frequently asked questions
How often should I run this checklist on the VPS?
Use it during initial preparation, before every relevant release and after any incident. For maintenance, review critical points monthly: updates, open ports, failed logins, disk usage, backup status and alerts. Adjust frequency based on service criticality.
Does changing port 22 increase SSH security?
Changing the port reduces noise from automated scans but does not replace essential measures: key authentication, disabling passwords and root, limiting attempts and ideally restricting by IP or VPN. Use port changes only as a complement, not the main defense.
Is UFW sufficient or should I use iptables directly?
For most cases, UFW is sufficient and less error-prone; it ultimately manages iptables underneath. If you need advanced rules, multiple tables or packet marking, configure iptables or nftables directly, and maintain documentation and regression tests.
How do I validate that backups really work?
Perform test restores in an isolated environment. Verify file integrity, database consistency and that services come back online. Document recovery time and steps. Automate success checks for jobs and alert on failures or exceeded recovery windows.
Which metrics should I monitor on a production VPS?
Monitor CPU, memory, disk usage and IO, network latency, connections, application errors, response times, queues and error rates per endpoint. Add alerts for full disk, OOM, service outages, certificates nearing expiry and failed backup jobs.
Need help applying this checklist to your VPS?
We can implement hardening, backups and monitoring with documented, reproducible scripts tailored to your environment.
Talk about hardening & maintenanceSee also
- Pre-deploy checklist and quick rollback plan for PHP applications in production
- Security checklist for CI/CD pipelines with self‑hosted runners and secrets management
- Guide: Centralize logs with Fluentd and Elasticsearch for PHP apps on a VPS
- Guide: Organizing PostgreSQL Migrations and Rollbacks for Small Teams