A lot of people spin up a VPS, set up a firewall and SSH keys, and figure theyโre done with security. But firewalls only keep the outside world from knocking on the door. What really decides how safe your server actually is comes down to how your data is stored, whether itโs encrypted in transit, and whether your backups are solid.
Know where your data lives and who can reach it
The first thing you have to accept is that your VPS provider has physical access to the storage underneath your server. That doesnโt mean theyโre snooping on your data โ most decent providers have strict privacy policies and donโt go looking โ but technically they could. They can also be legally required to hand things over if asked.
The takeaway is simple: if youโre storing anything sensitive, you canโt just trust the providerโs word. You need to handle encryption yourself at the application level.
Disk encryption: protecting data at rest
Disk encryption makes sure that even if someone gets their hands on the actual storage drives, the data is unreadable without the key. On Linux the standard tool for this is LUKS.
To encrypt a new data partition:
sudo apt install cryptsetup -y
# Encrypt partition (replace /dev/sdb with your target partition)
sudo cryptsetup luksFormat /dev/sdb
# Open the encrypted partition
sudo cryptsetup luksOpen /dev/sdb encrypted_data
# Format and mount
sudo mkfs.ext4 /dev/mapper/encrypted_data
sudo mount /dev/mapper/encrypted_data /mnt/secure_data
One practical gotcha: if you encrypt the system disk on a VPS, the server wonโt boot automatically after a reboot because it needs the passphrase entered manually. Thatโs why most people only encrypt the data partition and leave the OS disk unencrypted for production use.
File-level encryption: protecting sensitive files
If you donโt need full-disk encryption, you can encrypt individual files with GPG:
# Encrypt a file
gpg --symmetric --cipher-algo AES256 sensitive_file.txt
# Decrypt a file
gpg --decrypt sensitive_file.txt.gpg > sensitive_file.txt
For folders that need to stay encrypted but be easy to work with, EncFS lets you mount an encrypted directory as a normal folder:
sudo apt install encfs -y
# Create an encrypted directory
encfs ~/.encrypted_store ~/secure_folder
# Unmount when done
fusermount -u ~/secure_folder
Database encryption: protecting application data
If youโre running a database on the VPS, sensitive fields should be encrypted in your application code before they ever hit the database โ donโt rely only on database permissions.
MySQL supports Transparent Data Encryption (TDE). You can enable it in my.cnf:
[mysqld]
early-plugin-load=keyring_file.so
keyring_file_data=/var/lib/mysql-keyring/keyring
innodb_encrypt_tables=ON
innodb_encrypt_logs=ON
For field-level encryption at the app layer, hereโs a simple Python example:
from cryptography.fernet import Fernet
# Generate key (store safely, never in the database)
key = Fernet.generate_key()
f = Fernet(key)
# Encrypt
encrypted = f.encrypt(b"sensitive data")
# Decrypt
decrypted = f.decrypt(encrypted)
Transmission encryption: protecting data in transit
Data needs protection while itโs moving, not just while itโs sitting still. A few basics:
All web services should use HTTPS. Letโs Encrypt makes this free and easy:
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d your_domain
Never expose databases to the public internet. Lock them down to localhost or specific IPs only:
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
# Find bind-address and set it to:
bind-address = 127.0.0.1
If services need to talk across servers, use TLS or tunnel through SSH:
# Access a remote database through an SSH tunnel
ssh -L 3307:localhost:3306 user@remote_server
# Then connect locally to 127.0.0.1:3307
Backup strategy: the last line of defense
Encryption reduces risk; backups are your safety net. Follow the 3-2-1 rule: three copies of your data, on two different types of media, with one copy off-site.
Hereโs a simple automated daily backup script that also encrypts and uploads everything:
nano ~/backup.sh
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/home/yourname/backups"
REMOTE="user@backup-server:/backups"
mkdir -p $BACKUP_DIR
# Back up database
mysqldump -u root -p'your_password' --all-databases | \
gzip > $BACKUP_DIR/db_$DATE.sql.gz
# Back up application data
tar -czf $BACKUP_DIR/app_$DATE.tar.gz /var/www /home/yourname/data
# Encrypt backup file
gpg --symmetric --cipher-algo AES256 \
--batch --passphrase 'your_encryption_key' \
$BACKUP_DIR/db_$DATE.sql.gz
# Upload to remote server
rsync -avz $BACKUP_DIR/ $REMOTE/
# Delete local backups older than 7 days
find $BACKUP_DIR -mtime +7 -delete
echo "Backup completed: $DATE"
Make it executable and schedule it:
chmod +x ~/backup.sh
crontab -e
# Add: run at 2am every day
0 2 * * * ~/backup.sh >> ~/backup.log 2>&1
If you want to push backups to S3-compatible storage, rclone makes it easy:
sudo apt install rclone -y
rclone config # Follow prompts to configure your storage service
rclone copy $BACKUP_DIR remote:bucket-name/backups/
Never hard-code sensitive information in your code
This is one habit a surprising number of people still miss. Database passwords, API keys, and private keys should never live directly in your code or config files โ especially not in Git.
Use environment variables instead:
nano ~/.env
DB_PASSWORD=your_secure_password
API_KEY=your_api_key
SECRET_KEY=your_secret_key
Then read them in your code:
import os
from dotenv import load_dotenv
load_dotenv()
db_password = os.getenv('DB_PASSWORD')
And make sure .env is ignored by Git:
echo ".env" >> .gitignore
Regularly review permissions and access logs
Security isnโt a โset it and forget itโ thing. Every once in a while, take a quick look at these:
See who has sudo rights:
grep -Po '^sudo.+:\K.*$' /etc/group | tr ',' '\n'
Check recent logins and failed attempts:
last -n 20
lastb -n 20 # failed login attempts
Look for files with suspicious permissions:
# Find world-writable files
find / -xdev -type f -perm -0002 2>/dev/null
# Find SetUID files
find / -xdev -type f -perm -4000 2>/dev/null
Summary
VPS security isnโt about one magic setting โ itโs layers. Disk and file encryption protect data at rest. TLS protects it in transit. Good backups give you a safety net. Permission hygiene limits exposure. Regular reviews catch problems before they become disasters.
No single layer is enough by itself, but together they create a solid foundation. Spending a few hours getting these basics right upfront is way less painful than cleaning up after something goes wrong later.