Our thinking


Backup self-hosted Hudu instance to Dropbox

I have recently switched over from IT Glue to Hudu for my documentation platform, and am running it as a self-hosted instance. As I have root access to my server, I’m able to take control over things like ensuring it’s backed up.

In order to create a backup of Hudu, there’s a docker-compose command that you can run, e.g.:

docker-compose exec -T db pg_dump -U postgres hudu_production > $file_name

This will take an SQL dump of the Hudu database and save it as $file_name – e.g. hudu.sql

On the Hudu Support site, they give examples of backing it up to Amazon AWS, but I need to back it up to Dropbox. In order to back up to Dropbox, you need some API credentials. You can get them by going to Dropbox and making a new App:

https://www.dropbox.com/developers/apps/create

To restrict the API key to have access to a single folder, instead of my entire Dropbox account, when I created the app, I set the access type to App folder instead of Full Dropbox. This automatically creates a folder called Apps at the top level of my Dropbox and then creates a folder inside the Apps folder names as per your app (e.g. in my case, Hudu Backups).

With the app credentials you can quickly and easily generate an access token. The access token has full access to everything in the App folder, but nothing else. The token however is only generated with a four-hour lifespan (14400 seconds) so it’s not going to be any good for unattended backups.

If you need a longer lived access method – e.g. for unattended backups, you can use the app credentials to generate a refresh token instead. This refresh token is a much longer lived (permanent, until it is revoked) token, however can not be used direct

Where do you get the refresh token and access token from?
First of all, you need to manually get the refresh token by authorising your app via the Dropbox website. This will give you a code that you can then use to generate an access token
Visit the following URL in your browser:
https://www.dropbox.com/oauth2/authorize?token_access_type=offline&response_type=code&client_id=<App key>

This will give you an access code once you have authorised your app. Use the access code to generate an access token with curl:

curl https://api.dropbox.com/oauth2/token \
--data code=<access code> \
--data grant_type=authorization_code \
--data client_id=<app key>" \
--data "client_secret=<app_secret>

This will give you some JSON code with an access token and a refresh token. You probably don’t need the access token immediately as it expires in 4 hours, however make note of the refresh token which is a long-lived token that will only expire when you revoke it.

Enter your app credentials and your refresh token into the following script. This script needs curl, which is installed by default just about everywhere and jq for processing the JSON output and I had to install it on my Ubuntu instance with sudo apt install jq

Hudu Backup Script

#!/bin/bash

# Get the current date in the ISO format
today=$(date -I)

# Assign the file name
file_name="hudu-$today.sql.gz"

# hudu2 directory
hudu_directory="/path/to/your/hudu2"

# App key and secret
# Get them from Dropbox at https://www.dropbox.com/developers/apps/
app_key="my_app_key"
app_secret="my_app_secret"

refresh_token="my_refresh_token"

# Use the refresh token to generate a new access token. Remember this token is only valid for 4 hours

response=$(curl https://api.dropbox.com/oauth2/token \
    --data "grant_type=refresh_token" \
    --data "refresh_token=$refresh_token" \
    --data "client_id=$app_key" \
    --data "client_secret=$app_secret") || { echo "Error: Failed to obtain access token. Error code: $?"; exit 1; }

# Extract the access token from the response
access_token=$(echo $response | jq -r '.access_token')

# Change to the /home/username/hudu2 directory
cd $hudu_directory || { echo "Error: Failed to change directory. Error code: $?"; exit 2; }

# Run the command and output the contents to the file, piping it through gzip to compress it (~90% saving)
docker-compose exec -T db pg_dump -U postgres hudu_production | gzip > $file_name || { echo "Error: Failed to execute pg_dump command. Error code: $?"; exit 3; }

# Use curl to upload the file to Dropbox
response=$(curl -X POST https://content.dropboxapi.com/2/files/upload \
    --header "Authorization: Bearer $access_token" \
    --header 'Dropbox-API-Arg: {"path":"/'$file_name'","mode":"overwrite","autorename":true,"mute":false}' \
    --header "Content-Type: application/octet-stream" \
    --data-binary "@$file_name") || { echo "Error: Failed to upload file to Dropbox. Error code: $?"; exit 4; }

name=$(echo $response | jq -r '.name')
size=$(echo $response | jq -r '.size')
echo "Successfully uploaded $name, size $size bytes."

Take note that this script needs to run as root (or at least the docker-compose portion of it does).

I then added a cron job to run this daily, e.g.:

sudo crontab -e
0 2 * * * /bin/bash /home/kai/bin/hudu-backup.sh

Leave a Reply