Automatisasi Database Migration dengan Docker: Backup, Restore, dan Versioning
Konsep Database Migration dalam Lingkungan Container
Database migration adalah proses mengelola perubahan skema database secara terstruktur — bukan sekadar menyalin data dari satu tempat ke tempat lain. Migration mencakup penambahan kolom baru, perubahan tipe data, pembuatan tabel, hingga modifikasi constraint yang harus diterapkan secara terkontrol di setiap environment.
Dalam lingkungan container, strategi migration memerlukan perhatian lebih. Container bersifat ephemeral: ketika container dihentikan atau dihapus, seluruh data di dalamnya ikut hilang kecuali kita mengonfigurasi volume persistent. Tanpa strategi yang tepat, perubahan skema bisa hilang begitu container restart.
Kita akan membahas tiga pilar utama dalam mengelola database migration di Docker. Pertama, backup — membuat salinan aman sebelum setiap perubahan skema sebagai safety net. Kedua, restore — kemampuan mengembalikan database ke state sebelumnya jika migration gagal. Ketiga, versioning — melacak setiap perubahan skema agar kita tahu persis versi mana yang sudah diterapkan di setiap environment.
Menyiapkan Lingkungan Docker untuk Database PostgreSQL
Langkah pertama adalah menyiapkan struktur proyek dan konfigurasi container. Kita akan menggunakan docker-compose.yml untuk mendefinisikan service PostgreSQL dengan volume persistent.
Buat struktur folder berikut:
project/
docker-compose.yml
migrations/
backups/Folder migrations/ akan menyimpan file SQL untuk perubahan skema, sementara backups/ digunakan untuk menyimpan hasil dump database sebelum migration dijalankan.
Berikut konfigurasi docker-compose.yml:
version: "3.8"
services:
db:
image: postgres:16-alpine
container_name: postgres_db
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: secretpass
POSTGRES_DB: appdb
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
- ./backups:/backups
- ./migrations:/migrations
volumes:
pgdata:Konfigurasi ini menggunakan named volume pgdata untuk persistensi data database. Folder backups/ dan migrations/ di-mount sebagai bind mount agar bisa diakses dari host maupun container. Base image postgres:16-alpine dipilih karena ukurannya ringan.

Gambar: Diagram arsitektur Docker yang menunjukkan hubungan antara Docker Client, Docker Host (termasuk containers dan volumes), serta Docker Registry — Sumber: [Docker Docs](https://docs.docker.com/get-started/overview/)
Setelah file siap, jalankan container:
docker-compose up -dVerifikasi koneksi dengan:
docker exec -it postgres_db psql -U appuser -d appdb -c "SELECT version();"Output akan menampilkan versi PostgreSQL yang berjalan, menandakan database sudah siap digunakan.
Membangun Script Backup dan Restore Database
Data Science with Python
Master the art of data analysis, visualization, and predictive modeling.
Backup adalah langkah wajib sebelum menjalankan migration. Jika perubahan skema menyebabkan error, kita bisa mengembalikan database ke state sebelum migration menggunakan file backup tersebut.
Script backup menggunakan pg_dump untuk menghasilkan file SQL dengan timestamp sebagai nama file. Flag --clean dan --if-exists memastikan script hasil dump bisa dijalankan ulang tanpa error — statement DROP akan menggunakan IF EXISTS. Flag --no-owner menghilangkan informasi kepemilikan agar file dump bisa di-restore di environment berbeda tanpa konflik permission.
Berikut script backup (backup.sh):
#!/bin/bash
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
BACKUP_DIR="/backups"
DB_NAME="appdb"
DB_USER="appuser"
BACKUP_FILE="${BACKUP_DIR}/backup_${DB_NAME}_${TIMESTAMP}.sql"
echo "[$(date)] Starting backup for database: ${DB_NAME}"
pg_dump -U "${DB_USER}" -d "${DB_NAME}" \
--clean \
--if-exists \
--no-owner \
--file="${BACKUP_FILE}"
if [ $? -eq 0 ]; then
echo "[$(date)] Backup successful: ${BACKUP_FILE}"
echo "File size: $(du -h ${BACKUP_FILE} | cut -f1)"
else
echo "[$(date)] Backup FAILED for database: ${DB_NAME}"
exit 1
fiScript restore (restore.sh) membaca file dump dan mengembalikan state database:
#!/bin/bash
BACKUP_DIR="/backups"
DB_NAME="appdb"
DB_USER="appuser"
LATEST_BACKUP=$(ls -t ${BACKUP_DIR}/backup_${DB_NAME}_*.sql 2>/dev/null | head -1)
if [ -z "${LATEST_BACKUP}" ]; then
echo "No backup file found in ${BACKUP_DIR}"
exit 1
fi
echo "[$(date)] Restoring from: ${LATEST_BACKUP}"
psql -U "${DB_USER}" -d "${DB_NAME}" -f "${LATEST_BACKUP}"
if [ $? -eq 0 ]; then
echo "[$(date)] Restore completed successfully"
else
echo "[$(date)] Restore FAILED"
exit 1
fiWorkflow yang direkomendasikan: jalankan backup.sh sebelum mengeksekusi migration baru. Jika migration berhasil, backup lama bisa dihapus. Jika migration gagal, jalankan restore.sh untuk kembali ke state sebelum perubahan.

Gambar: Diagram yang menunjukkan korelasi antara pg_dump untuk membuat backup, psql untuk restore dari plain-text SQL dump, dan pg_restore untuk restore dari archive format — Sumber: [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:PostgreSQL_dump_restore.svg)
Untuk menjalankan script dari host:
docker exec postgres_db bash /backups/backup.sh
docker exec postgres_db bash /backups/restore.shStrategi Versioning untuk Database Migration
Versioning memungkinkan kita melacak perubahan skema secara sistematis. Pendekatan paling sederhana adalah memberi nama file migration dengan prefix nomor versi, misalnya:
migrations/
V001__create_users_table.sql
V002__add_email_column.sql
V003__create_orders_table.sqlSetiap file berisi statement SQL yang merepresentasikan satu unit perubahan. File dijalankan secara berurutan berdasarkan nomor versi.
Agar proses ini bisa dilacak, kita memerlukan tabel schema_migrations di database:
CREATE TABLE IF NOT EXISTS schema_migrations (
version VARCHAR(10) PRIMARY KEY,
filename VARCHAR(255) NOT NULL,
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);Tabel ini mencatat migration mana yang sudah dijalankan. Sebelum mengeksekusi file migration, script akan memeriksa apakah versi tersebut sudah ada di tabel — jika sudah, file akan dilewati. Inilah yang membuat proses migration menjadi idempotent: bisa dijalankan berkali-kali tanpa menghasilkan error duplikasi.
Berikut script Python sederhana yang mengorkestrasi proses ini:
!pip install psycopg2-binary
import os
import psycopg2
MIGRATIONS_DIR = "/migrations"
DB_CONFIG = {
"host": "localhost",
"port": 5432,
"dbname": "appdb",
"user": "appuser",
"password": "secretpass",
}
conn = psycopg2.connect(**DB_CONFIG)
cur = conn.cursor()
cur.execute("""
CREATE TABLE IF NOT EXISTS schema_migrations (
version VARCHAR(10) PRIMARY KEY,
filename VARCHAR(255) NOT NULL,
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
""")
conn.commit()
cur.execute("SELECT version FROM schema_migrations")
applied = {row[0] for row in cur.fetchall()}
migration_files = sorted(
[f for f in os.listdir(MIGRATIONS_DIR) if f.endswith(".sql")]
)
for filename in migration_files:
version = filename.split("__")[0]
if version in applied:
print(f"[SKIP] {filename} — already applied")
continue
with open(os.path.join(MIGRATIONS_DIR, filename)) as f:
sql = f.read()
cur.execute(sql)
cur.execute(
"INSERT INTO schema_migrations (version, filename) VALUES (%s, %s)",
(version, filename),
)
conn.commit()
print(f"[DONE] {filename} — applied successfully")
cur.close()
conn.close()Script ini membaca folder migrations/, membandingkan dengan isi tabel schema_migrations, dan hanya mengeksekusi file yang belum diterapkan. Pendekatan ini cocok untuk proyek skala kecil hingga menengah. Untuk proyek yang lebih kompleks, tools seperti Flyway atau Alembic menyediakan fitur tambahan seperti rollback otomatis dan checksum validation.
Mengotomatisasi Pipeline dengan Docker Entrypoint Script
Kita bisa mengintegrasikan seluruh workflow — backup, migration, dan verifikasi — ke dalam satu entrypoint script yang berjalan saat container startup. Dengan cara ini, proses migration menjadi bagian dari siklus hidup container.
Berikut entrypoint.sh:
#!/bin/bash
set -e
echo "=== Starting Database Migration Pipeline ==="
if [ "${AUTO_MIGRATE}" = "true" ]; then
echo "[1/3] Running pre-migration backup..."
bash /backups/backup.sh
echo "[2/3] Running database migrations..."
python3 /migrations/run_migrations.py
echo "[3/3] Verifying latest migration..."
psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -c \
"SELECT version, filename, applied_at FROM schema_migrations ORDER BY version DESC LIMIT 1;"
echo "=== Migration Pipeline Complete ==="
else
echo "AUTO_MIGRATE is not set to 'true'. Skipping migration."
fi
exec "$@"Environment variable AUTO_MIGRATE=true berfungsi sebagai safety switch. Di environment production, kita tidak ingin migration berjalan otomatis setiap container restart. Flag ini memastikan migration hanya berjalan ketika secara eksplisit diaktifkan.
Integrasikan entrypoint ke dalam Dockerfile:
FROM postgres:16-alpine
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
COPY backups/backup.sh /backups/backup.sh
COPY backups/restore.sh /backups/restore.sh
COPY migrations/ /migrations/
RUN chmod +x /usr/local/bin/entrypoint.sh \
&& chmod +x /backups/backup.sh \
&& chmod +x /backups/restore.sh \
&& apk add --no-cache python3 py3-pip \
&& pip3 install psycopg2-binary --break-system-packages
ENTRYPOINT ["entrypoint.sh"]
CMD ["postgres"]Saat container dijalankan dengan AUTO_MIGRATE=true, pipeline akan mengeksekusi backup terlebih dahulu, lalu menjalankan seluruh file migration yang belum diterapkan, dan terakhir memverifikasi hasilnya dengan query ke tabel schema_migrations. Query verifikasi menampilkan migration terakhir yang sukses — memastikan tidak ada file yang terlewat atau gagal.
Kuasai skill DevOps dan containerization secara menyeluruh melalui program Docker & DevOps di Rumah Coding. Program ini mencakup CI/CD pipeline, container orchestration dengan Docker Swarm dan Kubernetes, serta infrastructure as code menggunakan Terraform — semua dengan studi kasus production-ready.
Kursus Terkait
Data Science with Python
Master the art of data analysis, visualization, and predictive modeling.
E-commerce Sales Dashboard
- Data Cleaning Pipeline
- Interactive Charts
- Sales Forecasting Model