1. The Strategy: "The Prefix Bridge"
To use one Bitwarden project for all your Docker stacks, we use folder-based prefixes.
Bitwarden Key Format: STACKNAME_VARIABLE (e.g., PAPRA_AUTH_SECRET)
Local Result: The script strips PAPRA_ and writes AUTH_SECRET=value to /opt/stacks/papra/stack.env.
2. The Final Script
File: /root/sync-secrets.sh (Make sure to chmod +x /root/sync-secrets.sh)
Bash
#!/bin/bash
# Load environment (Token + Notify command)
. $HOME/.bashrc
# --- CONFIGURATION ---
STACKS_ROOT="/opt/stacks"
USER_ID=1000
GROUP_ID=1000
DRY_RUN=false
# 1. Check for Dry-Run flag
if [ "$1" = "-d" ] || [ "$1" = "--dry-run" ]; then
DRY_RUN=true
echo "--- 🔍 DRY RUN MODE: No files will be modified ---"
fi
# 2. Global Error Handler
on_error() {
if [ "$DRY_RUN" = "false" ]; then
local exit_code=$?
echo "❌ Bitwarden Sync Failed on $(hostname). Exit Code: $exit_code" | notify "Bitwarden Sync Error"
fi
}
trap 'on_error' ERR
# 3. Sync Logic
if [ -z "$BWS_ACCESS_TOKEN" ]; then
echo "Error: BWS_ACCESS_TOKEN not found."
exit 1
fi
SECRETS_JSON=$(bws secret list)
for dir in "$STACKS_ROOT"/*/; do
FOLDER_NAME=$(basename "$dir")
PREFIX=$(echo "$FOLDER_NAME" | tr '[:lower:]' '[:upper:]')
# CHANGED: Now targeting .env instead of stack.env
TARGET_FILE="${dir}.env"
MATCHING_SECRETS=$(echo "$SECRETS_JSON" | jq -r --arg P "${PREFIX}_" '.[] | select(.key | startswith($P)) | "\(.key | sub($P; ""))=\(.value)"')
if [ -n "$MATCHING_SECRETS" ]; then
if [ "$DRY_RUN" = "true" ]; then
echo "MATCH FOUND for $FOLDER_NAME (Prefix: ${PREFIX}_)"
echo "$MATCHING_SECRETS" | sed 's/=.*/=********/'
else
echo "Updating $TARGET_FILE..."
echo "$MATCHING_SECRETS" > "$TARGET_FILE"
chown $USER_ID:$GROUP_ID "$TARGET_FILE"
chmod 644 "$TARGET_FILE"
fi
fi
done
echo "--- Sync Complete ---
3. Usage & Testing
How to use the Dry-Run option
Use this to see what secrets the script found without actually changing any files. It is the safest way to verify your Bitwarden prefixes are correct.
Bash
/root/sync-secrets.sh -d
How to send a Test Notification
To verify that your notify function and Mailrise/Pushover connection are working correctly:
Bash
echo "This is a test notification from the Bitwarden Sync logic." | notify "Test Notification"
4. Automation (The Cron Job)
This runs the sync every 30 minutes. It logs output to sync.log so you can review history if needed.
Run crontab -e
Add this line:
Bash
*/30 * * * * . $HOME/.bashrc; /bin/bash /root/sync-secrets.sh >> /root/sync.log 2>&1
5. Dockge Integration
To make your Docker containers use these secrets, update your docker-compose.yaml in Dockge:
Add env_file: stack.env under your service.
Map the variables using ${VARIABLE_NAME}.
Example:
YAML
services:
papra:
image: ghcr.io/papra-hq/papra:latest
env_file:
- stack.env
environment:
- AUTH_SECRET=${AUTH_SECRET}
Troubleshooting Tips
Permission Denied: Ensure the script is executable with chmod +x /root/sync-secrets.sh.
Missing Token: If the cron job fails with "Missing Token," ensure export BWS_ACCESS_TOKEN="..." is at the very bottom of your /root/.bashrc.
Prefix Miss: Remember that the folder name must match the Bitwarden prefix exactly (e.g., folder uptime matches secret UPTIME_KEY).