Skip to content

Commit 1ba5ef3

Browse files
author
John Rogers
committed
Enhance Rclone configuration with dynamic remote setup and update documentation
1 parent 6157d78 commit 1ba5ef3

File tree

4 files changed

+140
-42
lines changed

4 files changed

+140
-42
lines changed

.env.example

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,53 @@ PGADMIN_EMAIL=your_pgadmin_email@example.com
2727
PGADMIN_PASSWORD=your_pgadmin_password
2828

2929
# --- Backup Agent Configuration (Rclone & Retention) ---
30-
# Name of the rclone remote configured in your rclone.conf (e.g., "mygdrive", "s3remote")
31-
# Leave blank to disable cloud upload.
30+
# --- Backup Agent: Active Rclone Destination ---
31+
# Name of the rclone remote (configured below or manually in rclone.conf) to use for uploads.
32+
# This remote MUST be defined either via the RCLONE_REMOTE_<N> variables below OR manually in rclone_config/rclone.conf
33+
# Leave blank to disable cloud upload for this backup run.
3234
RCLONE_REMOTE_NAME=
3335

34-
# Path within the rclone remote where backups should be stored (e.g., "resolve_backups/production")
35-
# Leave blank to disable cloud upload.
36+
# Path within the *active* rclone remote where backups should be stored (e.g., "resolve_backups/production")
37+
# Leave blank if RCLONE_REMOTE_NAME is blank.
3638
RCLONE_REMOTE_PATH=
3739

3840
# Number of days to keep local backups in the ./backups volume (default is 7 if not set)
3941
BACKUP_RETENTION_DAYS=7
4042

41-
# --- Optional: Google Drive Service Account for Rclone ---
42-
# If RCLONE_REMOTE_NAME is set and you want to use Google Drive via Service Account:
43-
# Paste the *entire content* of your Google Cloud Service Account JSON key file here.
44-
# Ensure it's enclosed in single quotes if it contains special characters, or handle quoting appropriately.
45-
# Leave blank if not using Service Account or if rclone.conf is manually configured.
46-
RCLONE_SERVICE_ACCOUNT_CREDENTIALS=
43+
# --- Optional: Dynamic Rclone Remote Configuration ---
44+
# The entrypoint script can dynamically configure rclone remotes based on these variables.
45+
# Define remotes sequentially starting with N=1 (RCLONE_REMOTE_1_...).
46+
# The entrypoint will stop looking when it doesn't find RCLONE_REMOTE_<N>_NAME.
47+
48+
# --- Example 1: Google Drive using Service Account ---
49+
# RCLONE_REMOTE_1_NAME=my_google_drive_backup
50+
# RCLONE_REMOTE_1_TYPE=drive
51+
# # Paste the *entire content* of your Google Cloud Service Account JSON key file below.
52+
# # Ensure it's enclosed in single quotes if it contains special characters, or handle quoting appropriately.
53+
# RCLONE_REMOTE_1_PARAM_SERVICE_ACCOUNT_CREDENTIALS='{ "type": "service_account", ... }'
54+
# # Optional: Specify a Team Drive ID if needed
55+
# # RCLONE_REMOTE_1_PARAM_TEAM_DRIVE=YOUR_TEAM_DRIVE_ID
56+
57+
# --- Example 2: AWS S3 ---
58+
# RCLONE_REMOTE_2_NAME=my_s3_backup
59+
# RCLONE_REMOTE_2_TYPE=s3
60+
# RCLONE_REMOTE_2_PARAM_PROVIDER=AWS # Or other S3-compatible provider
61+
# RCLONE_REMOTE_2_PARAM_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY_ID
62+
# RCLONE_REMOTE_2_PARAM_SECRET_ACCESS_KEY=YOUR_AWS_SECRET_ACCESS_KEY
63+
# RCLONE_REMOTE_2_PARAM_REGION=us-east-1 # Or your desired AWS region
64+
# # Optional: Specify storage class, ACL, etc.
65+
# # RCLONE_REMOTE_2_PARAM_STORAGE_CLASS=STANDARD_IA
66+
# # RCLONE_REMOTE_2_PARAM_ACL=private
67+
68+
# --- Example 3: Backblaze B2 ---
69+
# RCLONE_REMOTE_3_NAME=my_b2_backup
70+
# RCLONE_REMOTE_3_TYPE=b2
71+
# RCLONE_REMOTE_3_PARAM_ACCOUNT=YOUR_B2_ACCOUNT_ID_OR_APPLICATION_KEY_ID
72+
# RCLONE_REMOTE_3_PARAM_KEY=YOUR_B2_APPLICATION_KEY
73+
# # Optional: Specify endpoint if needed (e.g., for specific regions)
74+
# # RCLONE_REMOTE_3_PARAM_ENDPOINT=s3.us-west-000.backblazeb2.com
75+
76+
# --- Example 4: Local Filesystem (useful for testing) ---
77+
# RCLONE_REMOTE_4_NAME=local_test_backup
78+
# RCLONE_REMOTE_4_TYPE=local
79+
# # No parameters usually needed for 'local' type unless specifying nounc, case_sensitive etc.

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,7 @@ Thumbs.db
3333
*.swo
3434

3535
# Coverage
36-
/backups
36+
/backups
37+
38+
#plan
39+
PLAN_rclone_config.md

README.md

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ graph TD
2727
BackupAgent["laterbase-backup-agent<br>(Hourly pg_dump, rclone upload, retention)"]
2828
PgAdminUI[("laterbase-pgadmin<br>pgAdmin 4 UI")]
2929
BackupVolume["Local Backups backups"]
30-
RcloneConfigVolume[Rclone Config<br>./rclone_config]
30+
RcloneConfigVolume[Rclone Config<br>./rclone_config<br>(Dynamically Generated)]
3131
end
3232
3333
subgraph "Cloud Storage (Optional)"
@@ -65,9 +65,15 @@ graph TD
6565
* Replace `YOUR_PGADMIN_PASSWORD_HERE` with the password you want for the pgAdmin login.
6666
* Adjust `PRIMARY_PORT` or `PRIMARY_USER` if they differ from the defaults (5432, postgres).
6767
* Optionally, uncomment and set `POSTGRES_USER`, `POSTGRES_DB`, or `PGDATA` under the "Standby Server Configuration" section to override the defaults used by the standby service.
68-
* **Rclone Upload (Optional):**
69-
* Set `RCLONE_REMOTE_NAME` to the name of the remote you have configured in your `rclone.conf` file (e.g., `mygdrive`, `s3remote`). Leave blank to disable uploads.
70-
* Set `RCLONE_REMOTE_PATH` to the directory path within your rclone remote where backups should be stored (e.g., `resolve_backups/production`). Leave blank to disable uploads.
68+
* **Backup Agent - Active Rclone Destination (Optional):**
69+
* Set `RCLONE_REMOTE_NAME` to the name of the rclone remote you want the backup agent to **use** for uploads (e.g., `my_google_drive_backup`, `my_s3_backup`). This remote must be defined either dynamically (see next section) or manually in `./rclone_config/rclone.conf`. Leave blank to disable cloud uploads.
70+
* Set `RCLONE_REMOTE_PATH` to the directory path within the *active* rclone remote where backups should be stored (e.g., `resolve_backups/production`). Leave blank if `RCLONE_REMOTE_NAME` is blank.
71+
* **Backup Agent - Dynamic Rclone Remote Configuration (Optional):**
72+
* Instead of manually creating `./rclone_config/rclone.conf`, you can define remotes directly in the `.env` file using a specific format. The backup agent's entrypoint script will automatically create the necessary configuration inside the container when it starts.
73+
* Use the `RCLONE_REMOTE_<N>_...` variables as shown in `.env.example` (where `N` is a number starting from 1).
74+
* Define `RCLONE_REMOTE_<N>_NAME` (e.g., `my_s3_backup`) and `RCLONE_REMOTE_<N>_TYPE` (e.g., `s3`).
75+
* Add provider-specific parameters using `RCLONE_REMOTE_<N>_PARAM_<KEY>` (e.g., `RCLONE_REMOTE_1_PARAM_ACCESS_KEY_ID=...`). The `<KEY>` should be the lowercase version of the parameter name rclone expects (e.g., `access_key_id`, `secret_access_key`, `service_account_credentials`). Refer to `rclone config create --help` or the rclone documentation for specific provider parameters.
76+
* See `.env.example` for detailed examples for Google Drive, S3, B2, etc.
7177
* **Backup Retention (Optional):**
7278
* Set `BACKUP_RETENTION_DAYS` to the number of days you want to keep local backups in the `./backups` directory. Defaults to 7 if not set.
7379

@@ -125,14 +131,26 @@ graph TD
125131
mkdir backups
126132
```
127133

128-
4. **Rclone Configuration (Optional):**
129-
* If you want to use the cloud upload feature:
130-
* Create a directory named `rclone_config` in the same directory as `docker-compose.yml`:
134+
4. **Rclone Configuration (Optional - Choose One Method):**
135+
136+
* **Method A: Dynamic Configuration via `.env` (Recommended)**
137+
* Define your desired rclone remotes directly in the `.env` file using the `RCLONE_REMOTE_<N>_...` variables as described in the `.env` File section above and shown in `.env.example`.
138+
* The `laterbase-backup-agent` container will automatically generate the necessary `/config/rclone.conf` file internally when it starts based on these environment variables.
139+
* You still need to create the host directory for persistence, although the file inside will be managed by the container:
140+
```bash
141+
mkdir rclone_config
142+
```
143+
* Ensure the `RCLONE_REMOTE_NAME` variable in `.env` matches one of the `RCLONE_REMOTE_<N>_NAME` values you defined.
144+
145+
* **Method B: Manual `rclone.conf` File**
146+
* If you prefer to manage the `rclone.conf` file manually or have complex configurations not easily represented by environment variables:
147+
* Create the directory:
131148
```bash
132149
mkdir rclone_config
133150
```
134-
* Place your configured `rclone.conf` file inside the `./rclone_config/` directory. The backup container will mount this directory read-only at `/config/rclone/rclone.conf`. (Note: path inside container updated)
135-
* Ensure the remote name you use in `rclone.conf` matches the `RCLONE_REMOTE_NAME` set in your `.env` file.
151+
* Place your fully configured `rclone.conf` file inside `./rclone_config/`.
152+
* **Important:** If using this method, **do not** set any `RCLONE_REMOTE_<N>_...` variables in your `.env` file, as the entrypoint script might overwrite or conflict with your manual configuration.
153+
* Ensure the remote name you want to use matches the `RCLONE_REMOTE_NAME` set in your `.env` file.
136154
* **Security Note:** The `rclone.conf` file contains sensitive credentials. Ensure appropriate file permissions are set on the host machine (`chmod 600 ./rclone_config/rclone.conf`).
137155

138156
## Usage
@@ -172,7 +190,7 @@ graph TD
172190
* `app/prepare_primary_db.sh`: **(New)** Script to automate granting replication role and creating the replication slot on the primary server via `psql`. Run manually before starting services.
173191
* `app/setup_standby.sh`: Script run inside the standby container on first start to perform the initial base backup and configure replication.
174192
* `backup/backup.sh`: Script run by cron inside the backup agent container to perform hourly `pg_dump` backups, optionally upload via rclone, and manage retention.
175-
* `backup/entrypoint.sh`: Entrypoint for backup container, sets up cron.
193+
* `backup/entrypoint.sh`: Entrypoint for backup container. Dynamically configures rclone based on `RCLONE_REMOTE_<N>_...` environment variables (if present) before starting the cron daemon (managed by Ofelia).
176194
* `backup/crontab.txt`: Defines the cron schedule for `backup.sh`.
177195
* `./backups/` (Directory to be created): Host directory where local backup files (`.sql.gz`) will be stored by the backup agent.
178196
* `./rclone_config/` (Directory to be created, optional): Host directory containing the `rclone.conf` file for cloud uploads.

backup/entrypoint.sh

Lines changed: 65 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,80 @@ mkdir -p /config
1111
touch "$CONFIG_FILE"
1212
# Logging will go to stdout/stderr now
1313

14-
# Check if remote name and credentials are provided
15-
if [ -n "$RCLONE_REMOTE_NAME" ] && [ -n "$SERVICE_ACCOUNT_CREDS" ]; then
16-
echo "$(date): RCLONE_SERVICE_ACCOUNT_CREDENTIALS detected. Attempting to configure rclone remote '$RCLONE_REMOTE_NAME'." >&1
14+
# --- Dynamic Rclone Configuration Loop ---
15+
echo "$(date): Starting dynamic rclone configuration..." >&1
16+
N=1
17+
while true; do
18+
# Construct variable names for the Nth remote
19+
name_var_name="RCLONE_REMOTE_${N}_NAME"
20+
type_var_name="RCLONE_REMOTE_${N}_TYPE"
1721

18-
# Check if the remote already exists in the config file
19-
# Use rclone config show to check for a specific remote
20-
if ! rclone config show "$RCLONE_REMOTE_NAME:" --config "$CONFIG_FILE" > /dev/null 2>&1; then
21-
echo "$(date): Remote '$RCLONE_REMOTE_NAME' not found in $CONFIG_FILE. Creating..." >&1
22+
# Use indirect expansion to get the values
23+
remote_name="${!name_var_name}"
24+
remote_type="${!type_var_name}"
2225

23-
# Create the remote using service account credentials
24-
# Note: rclone expects the credentials directly, not a file path here
26+
# If NAME is not set, we've processed all defined remotes
27+
if [ -z "$remote_name" ]; then
28+
echo "$(date): No more RCLONE_REMOTE_${N}_NAME found. Configuration loop finished." >&1
29+
break
30+
fi
31+
32+
# TYPE is mandatory if NAME is set
33+
if [ -z "$remote_type" ]; then
34+
echo "$(date): Error: RCLONE_REMOTE_${N}_NAME ('$remote_name') is set, but RCLONE_REMOTE_${N}_TYPE is missing. Skipping remote ${N}." >&2
35+
N=$((N + 1))
36+
continue
37+
fi
38+
39+
echo "$(date): Processing remote ${N}: Name='$remote_name', Type='$remote_type'" >&1
40+
41+
# Check if the remote already exists
42+
if rclone config show "$remote_name:" --config "$CONFIG_FILE" > /dev/null 2>&1; then
43+
echo "$(date): Remote '$remote_name' already exists in $CONFIG_FILE. Skipping creation." >&1
44+
else
45+
# Remote does not exist, proceed with creation
46+
echo "$(date): Remote '$remote_name' not found in $CONFIG_FILE. Creating..." >&1
47+
declare -a rclone_params=() # Use an array for parameters
48+
49+
# Find all parameters for the current remote N
50+
param_prefix="RCLONE_REMOTE_${N}_PARAM_"
51+
# Use process substitution with env and grep for portability
52+
while IFS='=' read -r env_var_full env_var_value; do
53+
if [ -n "$env_var_value" ]; then # Ensure value is not empty
54+
# Extract the rclone key part (e.g., SERVICE_ACCOUNT_CREDENTIALS)
55+
rclone_key_upper=${env_var_full#"$param_prefix"}
56+
# Convert to lowercase for rclone config command
57+
rclone_key_lower=$(echo "$rclone_key_upper" | tr '[:upper:]' '[:lower:]')
58+
# Add the key=value pair to the array
59+
rclone_params+=("${rclone_key_lower}=${env_var_value}")
60+
fi
61+
done < <(env | grep "^${param_prefix}")
62+
63+
# Check if any parameters were found
64+
if [ ${#rclone_params[@]} -eq 0 ]; then
65+
echo "$(date): Warning: No parameters (RCLONE_REMOTE_${N}_PARAM_*) found for remote '$remote_name'. Creating with type only." >&2
66+
fi
67+
68+
# Construct and execute the command
69+
echo "$(date): Running: rclone config create \"$remote_name\" \"$remote_type\" [params...] --config \"$CONFIG_FILE\"" >&1
2570
rclone config create \
26-
"$RCLONE_REMOTE_NAME" \
27-
drive \
28-
scope=drive \
29-
service_account_credentials="$SERVICE_ACCOUNT_CREDS" \
71+
"$remote_name" \
72+
"$remote_type" \
73+
"${rclone_params[@]}" \
3074
--config "$CONFIG_FILE"
3175

76+
# Check exit status
3277
if [ $? -eq 0 ]; then
33-
echo "$(date): Successfully created rclone remote '$RCLONE_REMOTE_NAME' using Service Account." >&1
78+
echo "$(date): Successfully created rclone remote '$remote_name' (type: $remote_type)." >&1
3479
else
35-
echo "$(date): Error: Failed to create rclone remote '$RCLONE_REMOTE_NAME' using Service Account. Check credentials and permissions." >&2
80+
echo "$(date): Error: Failed to create rclone remote '$remote_name' (type: $remote_type). Check parameters and permissions." >&2
3681
# Consider exiting if creation fails: exit 1
3782
fi
38-
else
39-
echo "$(date): Rclone remote '$RCLONE_REMOTE_NAME' already exists in $CONFIG_FILE. Skipping creation." >&1
40-
fi
41-
else
42-
echo "$(date): RCLONE_SERVICE_ACCOUNT_CREDENTIALS not set or RCLONE_REMOTE_NAME is empty. Skipping dynamic rclone configuration." >&1
43-
fi
83+
fi # End if remote exists check
84+
85+
# Move to the next potential remote
86+
N=$((N + 1))
87+
done
4488

4589
echo "$(date): Entrypoint configuration check finished." >&1
4690
exit 0

0 commit comments

Comments
 (0)