A comprehensive backup script for GoToSocial instances that handles data export, SQLite database backup, and local media file backup with incremental storage using hard-links and optional encryption of sensitive data.
- Complete instance backup including:
- GoToSocial data export 🦥
- SQLite database backup with integrity checking ️🗃️
- Local media backup of attachments 📎 and custom emojis 🙂
- Incremental backups using hard-links to save space 🤏
- Backup structure that preserves directory hierarchy 📂
- Configurable retention period 📅
- Automatic log rotation ️♻️
- Support for both NixOS ️❄️ and traditional Linux distributions 🐧
- Optional encryption of sensitive data 🔐
- Optional failure notifications via ntfy.sh ️
⚠️ - Suitable for sending backups to untrusted remote storage using rsync or rclone 🚀
The script includes comprehensive error checking:
- Verifies root access
- Checks for required tools
- Validates database accessibility
- Performs SQLite integrity checks
- Verifies backup completeness
- Designed to be run in the root context
- There is no support for PostgreSQL databases
- There is no built-in support for sending backups to remote locations
- Use
rsync
,rclone
or similar tools to handle that
- Use
- Bash
- curl
- gzip
- SQLite3
- rsync
- OpenSSL
- Download the script:
curl -O https://raw.githubusercontent.com/wimpysworld/gotosocial-backup/gotosocial-backup.sh
- Make it executable:
chmod 700 backup-gotosocial.sh
chown root:root backup-gotosocial.sh
The script can be configured by copying gotosocial-backup.conf.example /etc/gotosocial-backup.conf
and editing the following variables accordingly:
# Root directory for backups
BACKUP_ROOT="/mnt/data/backup/gotosocial"
# Path to GoToSocial configuration file
GTS_CONFIG="/etc/gotosocial/config.yaml"
# Path to the GoToSocial SQLite database
GTS_DB="/var/lib/gotosocial/database.sqlite"
# Retention period in days
RETENTION_DAYS=28
# Encryption passphrase use to encrypt GtS exports and database
PASSPHRASE="mysupersecretpassphrase"
# Your ntfy instance
NTFY_SERVER="ntfy.sh"
# The topic you want to send gotosocial-backup failure alerts to
NTFY_TOPIC="mygotosocial-alerts"
On NixOS, the script automatically detects and uses the gotosocial-admin
wrapper that is available at /run/current-system/sw/bin/gotosocial-admin
. No additional configuration is needed.
On traditional Linux distributions, the script requires the gotosocial
binary to be in the system PATH and uses the specified configuration file path (GTS_CONFIG
).
Run the script as root:
sudo ./gotosocial-backup.sh
Add to root's crontab for automated backups:
sudo crontab -e
Add a line for hourly backups:
0 * * * * /path/to/gotosocial-backup.sh
For systems using systemd, you can create a timer unit to schedule backups. This provides better logging and monitoring capabilities compared to cron.
- Create the service unit file at
/etc/systemd/system/gotosocial-backup.service
:
[Unit]
Description=Backup GotoSocial database and local media
[Service]
ExecStart=/path/to/gotosocial-backup
User=root
- Create the timer unit file at
/etc/systemd/system/gotosocial-backup.timer
:
[Unit]
Description=Run GotoSocial backup periodically
[Timer]
OnBootSec=5min
OnUnitActiveSec=4h
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
Adjust the backup interval by changing OnUnitActiveSec
to the desired duration.
- Reload systemd to recognize new units
sudo systemctl daemon-reload
- Enable timer to run at boot
sudo systemctl enable gotosocial-backup.timer
- Start the timer
sudo systemctl start gotosocial-backup.timer
- Verify the timer is active:
sudo systemctl status gotosocial-backup.timer
- List all timers
sudo systemctl list-timers
- Monitor backup execution:
sudo journalctl -u gotosocial-backup.service
- Follow logs in real-time
sudo journalctl -u gotosocial-backup.service -f
The systemd timer configuration includes:
- Random delay of up to 5 minutes to prevent exact-hour execution
- Persistent timing to catch up on missed backups after system downtime
- Proper logging integration with journald
The script creates timestamped backup directories:
/mnt/data/backup/gotosocial/
├── 20241113_180102
│ ├── database.sqlite.gz.enc
│ ├── export.json.gz.enc
│ └── mnt
│ └── data
│ └── gotosocial
│ └── storage
│ ├── 01CCJ5GQF7A2KXVDPQ9Q9X23Q2
│ │ └── attachment
│ │ └── original
│ │ ├── 01JCF13JSSDJ0H8E9CWFFW2GVZ.png
│ │ └── 01JCF13K0R2HJDZWTPWGRNAPZ2.jpeg
│ ├── 01H7KYSZYN26CWTKT9JNKSZ2XB
│ │ ├── attachment
│ │ │ └── original
│ │ │ └── 01JCF1V56JDJ3SW03V82PFSQ37.png
│ │ └── emoji
│ │ └── original
│ │ └── 01JCJPE20Z1EYX3RTFBVTMM8JB.png
│ └── 01NATX9MGDJF8ESH0Q4TD5VAV7
│ └── attachment
│ └── original
│ ├── 01JCF2RK21FM491KRKSFTBFXWD.png
│ └── 01JCF2X4KN3032X5ZHRZ94H5MQ.png
├── backup.log
└── latest -> /mnt/data/backup/gotosocial/20241113_180102
Each backup includes:
- Compressed SQLite database backup (
database.sqlite.gz
) or encrypted (database.sqlite.gz.enc
) - Compressed GoToSocial export (
export.json.gz
) or encrypted (export.json.gz.enc
) - Local media files (attachments and emojis) with preserved directory structure
- A
latest
symlink pointing to the most recent successful backup
The script maintains a log file at $BACKUP_ROOT/backup.log
.
The log is automatically rotated to keep the most recent 4096 lines by default.
The script maintains backups according to these rules:
- Keeps all backups from the current day
- Keeps all backups newer than the retention period
- Removes backups that are both:
- Older than the retention period
- Not created on the current day
In this example:
- The backup root is:
/mnt/data/backup/gotosocial
- The backup timestamp is:
20241115_122300
- The database is:
/var/lib/gotosocial/database.sqlite
- The storage is:
/var/lib/gotosocial/storage/
- Source the configuration file
This step is necessary to set the environment variables to aid the recovery process.
source /etc/gotosocial-backup.conf
- Stop GoToSocial
sudo systemctl stop gotosocial
- Decrypt database and export file (if encrypted)
openssl enc -d -aes-256-cbc -pbkdf2 -in database.sqlite.gz.enc -out database.sqlite.gz -pass pass:"${PASSPHRASE}"
openssl enc -d -aes-256-cbc -pbkdf2 -in export.json.gz.enc -out export.json.gz -pass pass:"${PASSPHRASE}"
- Uncompress database and export files
gunzip database.sqlite.gz
gunzip export.json.gz
- Preserve the existing database
mv /var/lib/gotosocial/database.sqlite /var/lib/gotosocial/database.sqlite.old
- Restore the database
cp $BACKUP_ROOT/20241115_122300/database.sqlite /var/lib/gotosocial/database.sqlite
chown gotosocial:gotosocial /var/lib/gotosocial/database.sqlite
chmod 644 /var/lib/gotosocial/database.sqlite
- Restore the export (optional)
If your SQLite backup is intact, there is no need to restore the export file. However, if you need to restore the export file, you can do so using the following command:
gotosocial --config-path /etc/gotosocial/config.yaml admin import --path export.json
- Preserve existing media
mv /var/lib/gotosocial/storage /var/lib/gotosocial/storage.old
- Restore media files
rsync -av $BACKUP_ROOT/20241115_122300/var/lib/gotosocial/storage /var/lib/gotosocial/storage/
chown -R gotosocial:gotosocial /var/lib/gotosocial/storage
- Start GoToSocial
systemctl start gotosocial
Verify everything is working as expected:
- Check logs for errors:
journalctl -u gotosocial
- Verify web interface accessibility
- Test federation
These are some references that helped me create this script: