Skip to content
Current coupon:10% offUse it in your order

VPS backups: what we back up and how to make your own

How automatic VPS backups in the customer area work and when to restore them. Plus how to set up your own file and database backups with cron, rsync or restic following the 3-2-1 rule.

  • advanced
  • ~25 min
  • Updated 2 Oct 2026

We manage the VPS backups, and in the customer area you can restore the whole disk from them. For individual files, databases and a copy outside our infrastructure, you set up your own backups. We'll show both.

What you'll need

  • A StormHost VPS with Ubuntu 24.04 or Debian 13
  • An SSH connection as root (guide)
  • For backups off the VPS, a second computer or server with SSH access
  • About 25 minutes

Automatic StormHost backups#

We create VPS backups automatically. You'll find them in the customer area: My services › your VPS › Backups tab.

  • For each backup, the list shows Created (date and time), Size and the Storage it's kept on.
  • You can't create a backup manually from the customer area. Before a risky change (a system upgrade, a big configuration change), make your own backup as described in the rest of this guide.
  • If you see “One of the backup storages isn't responding right now — the list may be incomplete”, some backups are temporarily unavailable. Try again later, or contact us.
  • A member of a shared service only sees backups with the Backups and restore permission.

Restoring the whole VPS#

Restoring overwrites the whole disk

“Disk data will be irreversibly overwritten by the backup. The VPS shuts down during the restore.” Everything created on the VPS after the backup was made (new orders on your website, saved games, databases, logs) is lost. If the VPS holds anything like that, download it first (SFTP, mariadb-dump).

  1. In the Backups tab, find the backup by its date in the Created column.
  2. Click Restore.
  3. Read the warning and confirm it by moving the slider (“Slide to restore”).
  4. Wait for it to finish. During the restore the VPS is shut down and locked, and the console won't connect.
  5. After the restore, start the VPS (if it doesn't start by itself) and check your services.

Full restore, or individual files?#

SituationWhat to use
Broken system, compromised VPS, failed updateRestore the whole VPS in the Backups tab.
A deleted or overwritten file, a bad configuration editYour own file backup (below). Restoring the whole disk would roll back everything else too.
Damaged or deleted data in a databaseYour own database backup (a mariadb-dump export).
Moving to another VPS or another providerYour own backups, ideally stored off the VPS.

The 3-⁠2-⁠1 rule#

A proven rule for backups: 3 copies of your data, in 2 different places or storage types, with 1 of them outside the main infrastructure.

For a VPS that means:

  1. The data on the VPS (the original copy).
  2. Our automatic backups (the second copy, outside your VPS).
  3. Your own backup elsewhere: a home NAS, another server, cloud storage (the third copy, outside our infrastructure).

The third copy protects you even when a restore in the customer area won't help: you need an older state, a single file, or you want to move elsewhere.

What to back up#

Path / dataWhat it containsNote
/etcSystem and service configuration (Nginx, SSH, cron, firewall).Small; always back it up.
/home, /rootUsers' home directories, scripts, SSH keys./root/.ssh contains private keys, so encrypt the backup.
/var/wwwWebsites.
/opt, /srvApps and data you install there (for example Docker Compose projects).
DatabasesMariaDB/MySQL data.Always as an export (mariadb-dump), never by copying the /var/lib/mysql directory while it's running; that copy would be inconsistent.
Docker volumesContainer data (/var/lib/docker/volumes).Export databases running in containers; see Docker and Docker Compose.
Game server dataWorlds, configuration, plugins.The directory depends on your installation. Ideally save the server before backing up the world (save-all in Minecraft).
CrontabScheduled tasks.crontab -l > /root/crontab-zaloha.txt

Backup script#

Every day, the script exports all databases, archives the important directories and deletes backups older than 7 days.

  1. Create the script file:

    bash
    nano /usr/local/sbin/zaloha.sh
  2. Paste the content:

    /usr/local/sbin/zaloha.shbash
    #!/bin/bash
    set -euo pipefail
    umask 077
    
    CIL=/var/backups/vps
    DNES=$(date +%F)
    UCHOVAT_DNI=7
    
    mkdir -p "$CIL"
    
    # 1) databases (only if MariaDB/MySQL is installed)
    if command -v mariadb-dump >/dev/null 2>&1; then
      mariadb-dump --single-transaction --routines --events --all-databases | gzip > "$CIL/db-$DNES.sql.gz"
    fi
    
    # 2) files
    tar -czf "$CIL/soubory-$DNES.tar.gz" --ignore-failed-read /etc /home /root /var/www /opt /srv || [ $? -eq 1 ]
    
    # 3) delete old backups
    find "$CIL" -type f -name '*.gz' -mtime +"$UCHOVAT_DNI" -delete
    
    echo "$(date '+%F %T') backup done"
  3. Save (Ctrl+O, Enter), close (Ctrl+X) and make it executable:

    bash
    chmod 700 /usr/local/sbin/zaloha.sh
    • chmod 700 – only root can read and run the script.

What the script does:

LineMeaning
#!/bin/bashThe script runs with the bash interpreter.
set -euo pipefailThe script stops at the first error (-e), when an unset variable is used (-u) and when any command in a | pipeline fails (pipefail). That way you don't silently end up with a broken backup.
umask 077New files are readable by root only. Backups contain passwords and keys.
CIL=/var/backups/vpsThe backup directory on the VPS (CIL = “target”).
DNES=$(date +%F)Today's date in the form 2026-10-01 (DNES = “today”), used in file names (soubory = “files”).
UCHOVAT_DNI=7How many days to keep backups (UCHOVAT_DNI = “days to keep”). More days = more disk space.
command -v mariadb-dumpChecks whether the database is installed; if not, the export is skipped.
mariadb-dump … | gzip > …Exports all databases without locking tables, compressed on the fly. Root logs in to the database via unix socket without a password.
tar -czf … /etc /home …Packs and compresses the listed directories into one archive.
--ignore-failed-readDoesn't fail when a directory doesn't exist or can't be read.
|| [ $? -eq 1 ]tar returns code 1 when a file changed while it was being read (for example a log). That's fine; a real error (code 2) stops the script.
find … -mtime +7 -deleteDeletes archives older than 7 days.
  1. Try the script by hand:

    bash
    /usr/local/sbin/zaloha.sh
    ls -lh /var/backups/vps

    You'll see backup done and the files db-….sql.gz and soubory-….tar.gz. The message tar: Removing leading '/' from member names is normal: paths are stored in the archive without the leading slash.

Disk space

The backup sits on the same disk as the data. It takes up space, and if the disk fails or the VPS is compromised, it disappears along with the data. Keep an eye on free space (df -h) and copy backups off the VPS (see Copying off the VPS).

Running it automatically with cron#

  1. Make sure cron is installed (if it already is, nothing happens):

    bash
    apt -y install cron
  2. Open root's crontab:

    bash
    crontab -e

    On the first run, choose the nano editor.

  3. Add this line at the end:

    text
    30 3 * * * /usr/local/sbin/zaloha.sh >> /var/log/zaloha.log 2>&1
  4. Save and close the editor. You'll see crontab: installing new crontab.

A crontab line has five time fields and a command:

FieldAllowed valuesIn the exampleMeaning
minute0–⁠5930at minute 30
hour0–⁠233at 3 a.m.
day of month1–⁠31*every day
month1–⁠12*every month
day of week0–⁠7 (0 and 7 = Sunday)*every day of the week
command/usr/local/sbin/zaloha.shwhat to run (always with the full path)
redirection>> /var/log/zaloha.log 2>&1append output and errors to the log

More time examples:

EntryWhen it runs
0 */6 * * *every 6 hours (0:00, 6:00, 12:00, 18:00)
15 2 * * 0every Sunday at 2:15
0 4 1 * *on the first day of the month at 4:00

Times follow the VPS's time zone (timedatectl). Schedule backups for when the server has the least traffic.

Copying off the VPS#

Setting up SSH access to the target server#

Send the backups to another server (a home NAS with SSH, another VPS). In the examples, the target server has the IP address 198.51.100.25; use your own server's address instead.

On the target server (Ubuntu or Debian, logged in as root), create a user and a directory it can write to:

bash
adduser zaloha
mkdir -p /srv/zalohy
chown zaloha:zaloha /srv/zalohy
chmod 700 /srv/zalohy
  • adduser zaloha – creates the user zaloha. It asks for a password (you'll need it in step 2 below) and for optional details, which you can skip with Enter.
  • mkdir -p /srv/zalohy – the backup directory.
  • chown zaloha:zaloha – the directory belongs to the zaloha user so it can write there. Without this, both rsync and restic fail with Permission denied.
  • chmod 700 – nobody else can access the directory.

Password login must be allowed on the target server at least until you upload the key (step 2). On a NAS, follow its own instructions (user, shared folder, SSH enabled).

Then, on the VPS being backed up:

  1. Create a passwordless SSH key used only for backups:

    bash
    ssh-keygen -t ed25519 -f /root/.ssh/zaloha -N "" -C "zaloha-vps"
    • -t ed25519 – a modern key type.
    • -f /root/.ssh/zaloha – where to save the key (creates zaloha and zaloha.pub).
    • -N "" – no key passphrase, so cron can use it without you.
    • -C "zaloha-vps" – a key label.
  2. Upload the public key to the target server (you'll enter the password of the zaloha user on the target server):

    bash
    ssh-copy-id -i /root/.ssh/zaloha.pub [email protected]

    On the first connection SSH asks Are you sure you want to continue connecting (yes/no/[fingerprint])?. Type yes: the target server's fingerprint is saved to /root/.ssh/known_hosts, and cron won't be asked again.

  3. Create a connection shortcut. Open nano /root/.ssh/config and add:

    /root/.ssh/configtext
    Host zalohy
        HostName 198.51.100.25
        User zaloha
        IdentityFile /root/.ssh/zaloha
    • Host zalohy – the shortcut you'll use instead of the address.
    • HostName – the target server's IP address or domain.
    • User – the user on the target server.
    • IdentityFile – the key used to log in.
  4. Test the connection: ssh zalohy must log in without a password. Log out with exit.

Working with keys in detail: SSH key.

rsync#

rsync only copies changed files, so it's fast even with large backups. It must be installed on both sides (apt -y install rsync).

bash
rsync -az /var/backups/vps/ zalohy:/srv/zalohy/vps1/
  • -a – archive mode: copies recursively and preserves permissions, times and links.
  • -z – compresses data during the transfer.
  • /var/backups/vps/ – what to copy. The trailing slash means “the directory's contents”, not the directory itself.
  • zalohy:/srv/zalohy/vps1/ – the target: the shortcut from ~/.ssh/config and the directory on the target server.

Add the transfer as the last line of zaloha.sh, or to the crontab an hour after the backup:

text
30 4 * * * rsync -az /var/backups/vps/ zalohy:/srv/zalohy/vps1/ >> /var/log/zaloha.log 2>&1

Why without --delete

The --delete flag would delete everything on the target that's missing on the VPS. If an attacker or a mistake deletes the backups on the VPS, the next run would delete them on the target too. Delete old backups on the target server there instead, for example with your own cron job using find … -mtime +30 -delete.

Keep in mind that an attacker who takes over the VPS can also use the zaloha key directly and delete the backups on the target. Only a copy that can't be reached from the VPS protects against that: for example a target server that pulls the backups itself (rsync run on the target), or storage with version history.

restic (advanced)#

restic stores backups encrypted and with deduplication: repeated data is stored only once, so you can keep dozens of daily versions with little space. It can back up to an SFTP server as well as to cloud storage.

  1. Install restic: apt -y install restic.

  2. Create a file with the repository password and restrict its permissions:

    bash
    nano /root/.restic-heslo
    chmod 600 /root/.restic-heslo

    Put a long random password in the file (password generator).

Without the password you can't restore

restic encrypts the data. If you lose the password, nobody can decrypt the backups, not even you. Keep it off the VPS too (in a password manager).

  1. Create a repository on the target server:

    bash
    restic -r sftp:zalohy:/srv/zalohy/restic-vps1 --password-file /root/.restic-heslo init
    • -r sftp:zalohy:/srv/zalohy/restic-vps1 – a repository over SFTP; zalohy is the shortcut from ~/.ssh/config.
    • --password-file – the password file, so restic doesn't ask (required for cron).
    • init – creates a new empty repository. You'll see created restic repository ….
  2. Back up (you can put this command in the script instead of the tar archive; keep the database export):

    bash
    restic -r sftp:zalohy:/srv/zalohy/restic-vps1 --password-file /root/.restic-heslo backup /etc /home /root /var/www /opt /srv /var/backups/vps
  3. Delete old versions according to a policy:

    bash
    restic -r sftp:zalohy:/srv/zalohy/restic-vps1 --password-file /root/.restic-heslo forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
    • forget – removes versions (snapshots) the policy doesn't keep.
    • --keep-daily 7 – keeps the latest version from each of the last 7 days that have a backup.
    • --keep-weekly 4, --keep-monthly 6 – also one version per week for 4 weeks and one per month for 6 months.
    • --prune – immediately deletes data no version refers to anymore (frees up space).

Useful commands:

Command (with -r … and --password-file …)What it does
snapshotsLists stored versions with their dates.
restore latest --target /tmp/obnovaRestores the latest version into /tmp/obnova.
restore latest --target /tmp/obnova --include /etc/nginxRestores only the selected directory.
checkChecks the repository's integrity.

An alternative to restic is BorgBackup (documentation (opens in a new window)), which works on a similar principle.

Testing a restore#

A backup you've never tried to restore is just hope. Test it every now and then:

  1. List the archive's contents: tar -tzf /var/backups/vps/soubory-2026-10-01.tar.gz | head.

  2. Extract a single file into a temporary directory:

    bash
    mkdir -p /tmp/obnova
    tar -xzf /var/backups/vps/soubory-2026-10-01.tar.gz -C /tmp/obnova etc/nginx/nginx.conf
    • -x – extract, -z – gzip format, -f – from a file.
    • -C /tmp/obnova – where to extract (so the live file isn't overwritten).
    • etc/nginx/nginx.conf – what to extract (path without the leading slash).
  3. Check the database export: zcat /var/backups/vps/db-2026-10-01.sql.gz | head -n 20 must show the beginning of an SQL export.

  4. Do a full test (restoring the database, starting the app) on a separate test VPS, not on your production one.

Restoring a database overwrites the current data

The command gunzip < db-….sql.gz | mariadb restores all databases from the backup and overwrites their current contents. Only run it on a production VPS when you really want to bring back the state from the backup. Restoring a single database is covered in MariaDB on a VPS.

Verification#

  • The morning after the first night: tail /var/log/zaloha.log contains the line backup done.
  • ls -lh /var/backups/vps shows new files with today's date and a sensible size (not 0 bytes).
  • The target server has the same files (rsync), or restic snapshots shows today's version.

Troubleshooting#

Cron didn't run the script#

Check that cron is running and what it wrote to the log:

bash
systemctl status cron
journalctl -u cron -n 50

Common causes: the crontab is missing the full path to the script, the script isn't executable (chmod 700), or you edited the crontab of a user other than root (crontab -l prints it).

mariadb-dump: Got error: 1045: "Access denied for user …"#

The script isn't running as root, or the database root uses a password instead of the unix socket. Run the script from root's crontab. If root has a database password, store it in /root/.my.cnf, where mariadb-dump reads it automatically:

/root/.my.cnfini
[client]
user=root
password=<DB_ROOT_PASSWORD>
  • [client] – the option group for all MariaDB client programs (mariadb, mariadb-dump).
  • user, password – the credentials. Replace <DB_ROOT_PASSWORD> with the password of the database user root (without the angle brackets).

Secure the file with chmod 600 /root/.my.cnf so nobody else can read the password.

The backup is 0 bytes or suspiciously small#

The script failed partway through. Run it by hand and read the error. Thanks to set -euo pipefail, an export error doesn't go unnoticed.

Permission denied (publickey) with rsync or restic#

The target server didn't accept the key. Test ssh zalohy. If it asks for a password or fails, upload the key again (ssh-copy-id) and check that IdentityFile in /root/.ssh/config points to the right file.

restic: Fatal: wrong password or no key found#

The password in /root/.restic-heslo doesn't match the repository. Check that the file doesn't contain an extra space or a different password.

restic: Fatal: unable to open config file … Is there a repository at the following location?#

There's no repository at the given path (a typo in the path, or you didn't run init).

The disk is filling up#

Backups aren't being deleted, or they're too large. Check that the find … -delete step runs, lower UCHOVAT_DNI and leave out directories with large data that you back up another way.

Summary

  • We manage the VPS backups; in the Backups tab you restore the whole disk, and any newer data is lost.
  • Individual files and databases can only be restored from your own backups.
  • Back up databases with a mariadb-dump export, files with a tar archive or with restic.
  • Run the script with cron and send the backups off the VPS (rsync without --delete, restic with encryption).
  • Test restoring regularly.

Search the guides