Backopper is a small tool created in go to assist with the creation of backups for all the projects we got going on. Basically, it runs every x amount of time for each app and it creates the backups somewhere on disk (normally in /opt/backups/$APP). It also syncs the backups to a bucket in S3 and posts an archive of media files to a backup server.
There's 2 main functionalities to this tool: fetching a list of apps and creating the actual backups.
This is done via an API request to the a certain application. A response for this request would look like this:
[
{
"name": "project_1",
"frequency": "daily",
"db_engine": "mysql",
"project_type": "wordpress"
},
{
"name": "project_2",
"frequency": "weekly",
"db_engine": "postgres",
"project_type": "laravel"
}
]
There's 2 main things for each application that come in this response:
- The name of the application (which should match the name of the folder (if it's a serverpilot app) on disk (like /srv/users/serverpilot/apps/project_1))
- The frequency of the backups
It is based on jasonlvhit/gocron meaning that it will not create actual entries in the crontab but it will schedule the jobs necessary itself. Makes things a bit cleaner (though a bit harder to debug)
When the actual backup handler is run, several things happen:
- First, it looks for the
.env
file inside that app's root folder (hence the importance of the name of the app matching its location on disk). - Once it found it, it evaluates that
.env
file so that the script has access to all the database credentials. - After that, it composes a
mysqldump
orpg_dump
command based on those parameters. An example of that would look like so:mysqldump --user="$USER" --password="$PASSWORD" $DB_NAME | gzip > $BACKUPS_LOCATION/$UNIX_TIMESTAMP.sql.gz
. So, in the end, a new file that will have the name of the unix timestamp at the time of creating the backup will be created under/opt/backups/$APP
. I know that having the password in 'plain text' while executing the backup is not secure but this is the best solution without overengineering everything. - If the database dump is successful, it syncs a copy to an S3 bucket with a key like so:
$PROJECT_NAME/$ENVIRONMENT/backup_$TIMESTAMP.sql.gz
if it's a MySQL dump or$PROJECT_NAME/$ENVIRONMENT/backup_$TIMESTAMP.tar
if it's a PostgreSQL dump. - Regardless if the database dump is successful or not, it will attempt to create a media backup (generally things like
pictures that users upload, documents, etc). It is quite straightforward in the sense that it will simply run tar in the following format:
tar -czf $TEMPORARY_MEDIA_LOCATION -C $MEDIA_FOLDER $FOLDER_TO_BACK_UP
. A temporary location is used because the media backup can be quite heavy and it is not stored on the server itself (more on that later). The-C
flag is used to change the directory location to wherever the media folder is located, but not the actual folder to back up itself, that is$FOLDER_TO_BACK_UP
. It is a bit complex in the sense that it changes dirs to the parent folder to back up and then it performs the tar command but this is the most reliable way I could find of making it work. - If the media dump is successful, it is then sent via
scp
to the media server. A command like the following will be executed:scp -P$PORT $TEMPORARY_MEDIA_LOCATION $USER@$HOST:$MEDIA_BACKUPS_PATH
.