Daily Docker Backup — demo19 / Epris
Panduan lengkap stack backup Portainer (demo19-db + demo19-odoo):
deploy, env, backup, restore DB/full, jadwal, dan cara membuat script baru dari pola yang sama.
Snippet di bawah bisa Copy atau Edit langsung di halaman ini.
Lokal retention pg_dump + volume tar Cloud upload: fase berikutnya
~/scripts/v19_epris_docker_backup/Plan:
~/.cursor/plans/v19_epris_daily_docker_backup.plan.mdDeploy tipikal di host:
/opt/odoo-backup-demo19/ · backup root /var/backups/odoo-demo19/
1. Overview
Backup harian tidak mengandalkan tar mentah volume Postgres yang sedang hidup. Sumber kebenaran DB adalah logical dump; volume Odoo di-arsip read-only.
demo19-db → db.dump.gzdata_dir, customs, config, enterprise (helper Alpine, mount :ro)SHA256SUMS + symlink latestKEEP_DAILY| Volume | Isi | Cara backup |
|---|---|---|
odoo_db_data | PG data dir | Lewat pg_dump (tar mentah opsional, default off) |
odoo_data_dir | Filestore | tar harian |
odoo_customs | Custom addons | tar harian |
odoo_config | odoo.conf | tar harian |
odoo_enterprise | EE addons | tar harian |
demo19_odoo_data_dir). Selalu cocokkan dengan
docker volume ls di backup.env.
2. Struktur folder
v19_epris_docker_backup/ ├── bin/ │ ├── backup-demo19.sh # job harian │ ├── prune-demo19-backups.sh # retention │ ├── restore-demo19-db.sh # restore Postgres saja │ └── restore-demo19-full.sh # DB + 4 volume Odoo ├── config/ │ └── backup.env.example # → copy jadi backup.env (chmod 600) ├── systemd/ │ ├── odoo-demo19-backup.service │ └── odoo-demo19-backup.timer ├── docs/ │ └── index.html # dokumentasi ini └── README.md
3. Quick start (host Portainer)
sudo mkdir -p /opt/odoo-backup-demo19 /var/backups/odoo-demo19 /var/log
sudo cp -a ~/scripts/v19_epris_docker_backup/. /opt/odoo-backup-demo19/
sudo cp /opt/odoo-backup-demo19/config/backup.env.example \
/opt/odoo-backup-demo19/config/backup.env
sudo chmod 600 /opt/odoo-backup-demo19/config/backup.env
sudo chmod 700 /opt/odoo-backup-demo19/bin/*.sh
# Sesuaikan nama container & volume di host ini:
docker ps --format '{{.Names}}'
docker volume ls
sudo nano /opt/odoo-backup-demo19/config/backup.env
# Uji manual
sudo /opt/odoo-backup-demo19/bin/backup-demo19.sh
ls -lah /var/backups/odoo-demo19/latest/
sha256sum -c /var/backups/odoo-demo19/latest/SHA256SUMS
4. Config backup.env
Secret dan nama resource hidup di env, bukan di script.
Backup: argumen posisi path env.
Restore: --env-file PATH atau $BACKUP_ENV_FILE.
# --- Identity --- BACKUP_NAME=demo19 BACKUP_ROOT=/var/backups/odoo-demo19 KEEP_DAILY=7 MIN_FREE_GB=5 # --- Containers --- ODOO_CONTAINER=demo19-odoo DB_CONTAINER=demo19-db # --- Postgres (kosongkan password agar baca dari container) --- PGUSER=odoo PGPASSWORD= PGDATABASE=demo-odoo19 PGDUMP_FORMAT=custom # --- Volumes (cocokkan docker volume ls) --- VOL_DB_DATA=odoo_db_data VOL_DATA_DIR=odoo_data_dir VOL_CUSTOMS=odoo_customs VOL_CONFIG=odoo_config VOL_ENTERPRISE=odoo_enterprise STACK_COMPOSE_FILE= PACK_BUNDLE=0 HELPER_IMAGE=alpine:3.20 STOP_ODOO_DURING_FILESTORE=0 # BACKUP_RAW_DB_VOLUME=0
| Variabel | Arti |
|---|---|
KEEP_DAILY | Jumlah folder tanggal yang disimpan |
MIN_FREE_GB | Abort jika free disk di bawah ambang |
PGDUMP_FORMAT | custom → db.dump.gz; plain → db.sql.gz |
STOP_ODOO_DURING_FILESTORE | 1 = stop Odoo sebentar saat tar filestore |
PACK_BUNDLE | 1 = buat demo19_YYYY-MM-DD.tar |
5. Backup
# default: config/backup.env di tree script sudo /opt/odoo-backup-demo19/bin/backup-demo19.sh # atau path env eksplisit sudo /opt/odoo-backup-demo19/bin/backup-demo19.sh \ /opt/odoo-backup-demo19/config/backup.env
- Cek Docker daemon, container DB running, free disk
- Resolve
PG*dari env file atauprintenvdi container DB pg_dump→ gzip- Tar 4 volume via
docker run alpine … tar czf - Tulis
MANIFEST.txt,SHA256SUMS, updatelatest - Panggil
prune-demo19-backups.sh
sudo /opt/odoo-backup-demo19/bin/prune-demo19-backups.sh \ /opt/odoo-backup-demo19/config/backup.env
6. Restore
pg_restore --clean. Volume odoo_db_data tidak di-overwrite;
DB dipulihkan lewat artifact dump.
# folder tanggal atau symlink latest sudo /opt/odoo-backup-demo19/bin/restore-demo19-full.sh \ /var/backups/odoo-demo19/2026-10-21 # tanpa prompt (otomasi / drill) sudo /opt/odoo-backup-demo19/bin/restore-demo19-full.sh \ /var/backups/odoo-demo19/latest --yes # env di lokasi non-default sudo /opt/odoo-backup-demo19/bin/restore-demo19-full.sh \ /var/backups/odoo-demo19/latest \ --env-file /opt/odoo-backup-demo19/config/backup.env \ --yes
# minta ketik YES secara interaktif sudo /opt/odoo-backup-demo19/bin/restore-demo19-db.sh \ /var/backups/odoo-demo19/latest/db.dump.gz \ --env-file /opt/odoo-backup-demo19/config/backup.env # non-interaktif printf 'YES\n' | sudo /opt/odoo-backup-demo19/bin/restore-demo19-db.sh \ /var/backups/odoo-demo19/latest/db.dump.gz \ --env-file /opt/odoo-backup-demo19/config/backup.env
DB-only tidak mengembalikan filestore/customs. Attachment hilang → pakai full restore.
| Flag | Efek |
|---|---|
--yes / -y | Skip prompt (full only) |
--skip-db | Hanya volume |
--skip-volumes | Hanya database |
--env-file PATH | Lokasi backup.env |
7. Jadwal
30 2 * * * root /opt/odoo-backup-demo19/bin/backup-demo19.sh >> /var/log/odoo-demo19-backup.log 2>&1
sudo cp /opt/odoo-backup-demo19/systemd/odoo-demo19-backup.* /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now odoo-demo19-backup.timer systemctl list-timers | grep odoo-demo19 # uji sekali: sudo systemctl start odoo-demo19-backup.service journalctl -u odoo-demo19-backup.service -n 50 --no-pager
Timer default: 02:30 + RandomizedDelaySec=5m.
8. Verifikasi setelah backup / restore
LATEST=/var/backups/odoo-demo19/latest ls -lah "$LATEST" cat "$LATEST/MANIFEST.txt" ( cd "$LATEST" && sha256sum -c SHA256SUMS ) # Setelah restore full: docker ps --filter name=demo19 # buka UI Odoo → apps/customs → sample attachment (filestore)
9. Membuat script baru dari pola ini
Kalau targetnya stack lain (bukan demo19), clone kerangka — jangan tulis dari nol.
SRC=~/scripts/v19_epris_docker_backup
DST=~/scripts/v18_tlt_docker_backup # ganti nama project
cp -a "$SRC" "$DST"
cd "$DST"
# rename file demo19 → tlt (contoh)
rename 's/demo19/tlt/' bin/* systemd/* 2>/dev/null || \
for f in bin/*demo19* systemd/*demo19*; do
mv "$f" "${f//demo19/tlt}"
done
# edit identitas di env example
sed -i 's/demo19/tlt/g; s/odoo-demo19/odoo-tlt/g' config/backup.env.example
# lalu: cp config/backup.env.example config/backup.env && nano config/backup.env
10. Pola bash (template)
#!/usr/bin/env bash
# Deskripsi singkat job.
# Usage: my-script.sh [/path/to/backup.env]
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
ENV_FILE="${1:-${ROOT_DIR}/config/backup.env}"
log() { printf '[%s] %s\n' "$(date -Is)" "$*"; }
die() { log "ERROR: $*"; exit 1; }
[[ -f "${ENV_FILE}" ]] || die "Missing env file: ${ENV_FILE}"
# shellcheck disable=SC1090
source "${ENV_FILE}"
: "${BACKUP_ROOT:?}"
: "${DB_CONTAINER:?}"
: "${ODOO_CONTAINER:?}"
command -v docker >/dev/null || die "docker CLI not found"
docker info >/dev/null 2>&1 || die "Cannot talk to Docker daemon"
log "=== start ==="
# … kerja di sini …
log "=== done ==="
Konvensi CLI
- Backup / prune: path env sebagai argumen posisi
- Restore: path artifact +
--env-file+ flag perilaku - Jangan hardcode password di script atau di unit systemd (pakai file env
chmod 600)
11. Sandbox testing
Di WSL tanpa Docker daemon, uji logika dengan mock docker + Postgres lokal
(contoh drill: /tmp/epris_backup_sandbox).
PATH, volume palsu, backup.env sandboxsha256sum -c SHA256SUMSYES) → assertSANDBOX=/tmp/epris_backup_sandbox SCRIPTS=~/scripts/v19_epris_docker_backup export EPRIS_SANDBOX="$SANDBOX" export PATH="$SANDBOX/mock-bin:$PATH" "$SCRIPTS/bin/backup-demo19.sh" "$SANDBOX/backup.env" "$SCRIPTS/bin/restore-demo19-full.sh" "$SANDBOX/backups/latest" \ --env-file "$SANDBOX/backup.env" --yes printf 'YES\n' | "$SCRIPTS/bin/restore-demo19-db.sh" \ "$SANDBOX/backups/latest/db.dump.gz" --env-file "$SANDBOX/backup.env"
12. Isi satu hari backup
/var/backups/odoo-demo19/
2026-10-21/
MANIFEST.txt
SHA256SUMS
backup.log
db.dump.gz # atau db.sql.gz
vol_odoo_data_dir.tar.gz
vol_odoo_customs.tar.gz
vol_odoo_config.tar.gz
vol_odoo_enterprise.tar.gz
stack.compose.yml # jika STACK_COMPOSE_FILE di-set
latest -> 2026-10-21/
13. CLI reference
| Command | Args utama |
|---|---|
backup-demo19.sh |
[/path/to/backup.env] |
prune-demo19-backups.sh |
[/path/to/backup.env] |
restore-demo19-db.sh |
/path/to/db.dump.gz|db.sql.gz [--env-file PATH] |
restore-demo19-full.sh |
/path/to/dated-dir [--env-file] [--yes] [--skip-db] [--skip-volumes] |
14. Troubleshooting
| Gejala | Cek |
|---|---|
Missing env file | Copy example → backup.env; restore pakai --env-file |
Cannot talk to Docker daemon | User di group docker / jalankan sebagai root; engine hidup |
Volume not found | Prefix Portainer di docker volume ls vs env |
PGPASSWORD empty | Isi di env atau pastikan POSTGRES_PASSWORD di container DB |
pg_restore exit 1 | Sering benign dengan --clean; full script menerima 0 atau 1 |
| Free disk abort | Turunkan data / naikkan disk / sesuaikan MIN_FREE_GB |
| Attachment hilang setelah DB-only | Pakai restore-demo19-full.sh |
rclone / S3 / B2) + enkripsi di akhir backup — belum di-wire di script saat ini.