Aller au contenu
medium

Portabase : sauvegarder plusieurs bases de données depuis un dashboard

20 min de lecture

logo Portabase

Portabase est un outil open source (licence Apache 2.0) qui sauvegarde et restaure vos bases de données depuis un dashboard web que vous hébergez vous-même. Au lieu d'écrire un script cron par serveur et par moteur, vous pilotez PostgreSQL, MySQL, MongoDB, Redis ou des volumes Docker depuis une seule interface, avec un historique des sauvegardes et une restauration en quelques clics.

Ce guide déploie Portabase avec Docker Compose, connecte un agent, déclare une base PostgreSQL et une base MySQL, puis prouve le cycle complet : sauvegarde, incident simulé, restauration. Tout a été validé en lab sur la version 1.26.0, y compris le piège réseau qui bloque la plupart des premières installations.

À la fin de ce guide, vous aurez une instance Portabase fonctionnelle et vous saurez sauvegarder puis restaurer une base en toute confiance :

  • L'architecture de Portabase : un dashboard central et des agents au plus près des bases
  • Déployer le dashboard avec Docker Compose et éviter le piège de la variable PROJECT_URL
  • Créer le compte administrateur et parcourir l'assistant d'installation
  • Connecter un agent au dashboard grâce à son Edge Key
  • Déclarer une base PostgreSQL et une base MySQL à sauvegarder
  • Lancer une sauvegarde, simuler un incident, puis restaurer et vérifier les données

La sauvegarde d'une base isolée est un problème résolu : pg_dump pour PostgreSQL, mysqldump pour MySQL, un cron et un stockage. Ces méthodes natives restent la référence et sont détaillées dans les guides Sauvegarder PostgreSQL et Sauvegarder MySQL.

Le problème change d'échelle dès que vous gérez plusieurs bases, sur plusieurs serveurs, avec plusieurs moteurs. Chaque script vit dans son coin, personne ne sait quelle sauvegarde a réussi hier soir, et la première vraie restauration se fait sous stress. Portabase répond à ce besoin d'orchestration : il ne réécrit pas pg_dump, il l'appelle pour vous, centralise l'historique, la planification et la restauration, et vous donne une vue unique sur l'état de toutes vos sauvegardes.

Portabase sépare deux rôles, et comprendre cette séparation évite la moitié des erreurs de configuration :

  • Le dashboard est le cerveau. C'est l'interface web, la base de métadonnées (qui sauvegarde quoi, quand, avec quel résultat) et le point de pilotage. Il ne touche jamais directement vos bases de production.
  • L'agent est le bras. Vous l'installez au plus près des bases à protéger. C'est lui qui se connecte à PostgreSQL ou MySQL, lance le dump, l'envoie vers le stockage et exécute les restaurations. Il ne fait rien tant que le dashboard ne lui a pas donné d'ordre.

Entre les deux, un jeton d'enregistrement appelé Edge Key authentifie l'agent auprès du dashboard. Cette architecture permet de sauvegarder des bases réparties sur plusieurs machines ou plusieurs réseaux : un seul dashboard, autant d'agents que nécessaire.

Pour reproduire ce lab, il vous faut :

  • Docker et le plugin Docker Compose (v2) installés
  • Environ 1 Go de RAM libre pour le dashboard et sa base de métadonnées
  • Deux bases à sauvegarder. Le lab en fournit deux jetables, une PostgreSQL et une MySQL, avec un jeu de données minimal

Le lab reste entièrement local. Aucune donnée ne sort de votre poste.

Le dashboard a besoin de sa propre base PostgreSQL pour stocker ses métadonnées. On décrit les deux services dans un compose.yml.

# compose.yml : le dashboard Portabase et sa base de metadonnees
name: portabase-dashboard
services:
portabase:
container_name: portabase-app
image: portabase/portabase:1.26.0@sha256:69e97155330991d8c28cb12ea2967d87a202cf838cb77605b33eaea16a61d9ab
restart: always
env_file: .env
ports:
- "8887:80"
# Point cle du reseau : voir la section suivante
extra_hosts:
- "portabase.lab:host-gateway"
volumes:
- portabase-data:/data
networks:
- portabase
depends_on:
db:
condition: service_healthy
db:
container_name: portabase-pg
image: postgres:17-alpine@sha256:742f40ea20b9ff2ff31db5458d127452988a2164df9e17441e191f3b72252193
restart: always
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
networks:
- portabase
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
networks:
portabase:
name: portabase_network
volumes:
postgres-data:
portabase-data:

La configuration sensible vit dans un fichier .env à côté du compose.yml. Générez les secrets, ne les recopiez pas :

Fenêtre de terminal
# secrets a generer une seule fois
openssl rand -hex 32 # -> PROJECT_SECRET
openssl rand -hex 12 # -> POSTGRES_PASSWORD (a reporter dans DATABASE_URL)
# .env : configuration du dashboard
PROJECT_URL=http://portabase.lab:8887
PROJECT_SECRET=REMPLACER_PAR_LE_HEX_32
POSTGRES_USER=portabase
POSTGRES_PASSWORD=REMPLACER_PAR_LE_HEX_12
POSTGRES_HOST=db
POSTGRES_PORT=5432
POSTGRES_DB=portabase
DATABASE_URL=postgresql://portabase:REMPLACER_PAR_LE_HEX_12@db:5432/portabase

Le piège PROJECT_URL, la source de « Failed to fetch »

Section intitulée « Le piège PROJECT_URL, la source de « Failed to fetch » »

C'est le point qui bloque la première installation, et il mérite qu'on s'y arrête. La variable PROJECT_URL doit être joignable à l'identique depuis deux endroits différents :

  1. Le conteneur du dashboard s'appelle lui-même en rendu côté serveur (SSR). Pour lui, PROJECT_URL doit pointer vers une adresse qu'il peut atteindre de l'intérieur.
  2. Le navigateur appelle la même URL en JavaScript. Or le dashboard applique une politique de sécurité CSP connect-src 'self' : le navigateur n'autorise les appels que vers exactement l'origine de PROJECT_URL.

Si vous mettez http://localhost:8887, le navigateur est content mais le conteneur ne s'atteint pas lui-même sur le port 8887 (son application écoute sur le port 80 en interne). Si vous mettez une autre adresse, c'est le navigateur qui refuse l'appel avec un laconique « Failed to fetch » au moment de créer le compte.

La solution est un nom d'hôte unique, résolu de la même façon des deux côtés, sur le port publié :

  1. Côté navigateur, faites pointer ce nom vers votre machine dans /etc/hosts :

    Fenêtre de terminal
    echo "127.0.0.1 portabase.lab" | sudo tee -a /etc/hosts
  2. Côté conteneur, la ligne extra_hosts: ["portabase.lab:host-gateway"] du compose.yml résout ce même nom vers l'hôte Docker. Le conteneur atteint donc http://portabase.lab:8887 exactement comme le navigateur.

  3. PROJECT_URL=http://portabase.lab:8887 est alors valide des deux côtés, et la CSP est satisfaite.

Lancez le dashboard, puis vérifiez qu'il répond :

Fenêtre de terminal
docker compose up -d
curl -s -o /dev/null -w "%{http_code}\n" http://portabase.lab:8887/api/health
# -> 200

Ouvrez http://portabase.lab:8887 dans votre navigateur. Au premier lancement, Portabase propose de créer le compte administrateur. Renseignez un nom, une adresse et un mot de passe robuste : ce compte est superadmin, il pilote toute l'instance.

Création du compte administrateur Portabase 1.26

L'assistant enchaîne ensuite une série d'étapes rapides : sécurité du compte, préférences, création de l'organisation, notifications et stockage. Vous pouvez passer les étapes optionnelles (notifications, stockage externe) : un stockage local est configuré par défaut, suffisant pour ce lab. Poursuivez jusqu'à l'étape Create an agent.

L'assistant propose de créer un agent. Donnez-lui un nom (ici agent-lab) et validez. Portabase affiche alors l'écran Connect your agent avec la commande à exécuter et, surtout, l'Edge Key : le jeton qui autorise l'agent à rejoindre le dashboard.

Écran de connexion de l'agent avec l'Edge Key

L'Edge Key est un jeton signé qui encode notamment l'URL du dashboard. Copiez-le : il alimente la variable EDGE_KEY de l'agent. Créez un dossier agent/ avec son propre compose.yml.

# agent/compose.yml : l'agent Portabase
name: portabase-agent
services:
app:
container_name: portabase-agent
image: portabase/agent:latest@sha256:9a57cb6470bcf4e7583953d6ab576ab4ba414e57498d38e03c6805e926cc3ab0
restart: always
volumes:
- ./databases.json:/config/config.json
extra_hosts:
# l'Edge Key pointe le dashboard sur portabase.lab:8887 ;
# l'agent doit resoudre ce nom vers l'hote, comme le navigateur
- "localhost:host-gateway"
- "portabase.lab:host-gateway"
environment:
EDGE_KEY: "${EDGE_KEY}"
TZ: "Europe/Paris"
POLLING: 5
APP_ENV: production
LOG: info
networks:
- portabase
networks:
portabase:
name: portabase_network
external: true

L'agent lit la liste des bases dans un fichier config.json, monté ici depuis databases.json. Créez-le vide au premier démarrage, il sera complété à l'étape suivante :

Fenêtre de terminal
echo '{"databases": []}' > agent/databases.json

Placez l'Edge Key dans agent/.env, puis démarrez l'agent :

Fenêtre de terminal
cd agent
echo "EDGE_KEY=collez_ici_votre_edge_key" > .env
docker compose up -d
docker logs portabase-agent

Les journaux confirment la connexion :

INFO EDGE_KEY server_url: http://portabase.lab:8887
INFO EDGE_KEY agent_id: 7141e5e0-3e3a-4427-a915-0bf56f55b94e
INFO Ping server task started
INFO Databases: 0 instances loaded

Dans l'édition Community, les bases se déclarent dans le fichier de configuration de l'agent. Chaque entrée décrit une base : son moteur (type), son hôte, son port et ses identifiants. L'agent lit ce fichier et signale les bases au dashboard.

Voici le databases.json du lab, avec une base PostgreSQL et une base MySQL :

{
"databases": [
{
"name": "Boutique PostgreSQL",
"database": "boutique",
"type": "postgresql",
"username": "app",
"password": "app-mdp-demo",
"port": 5432,
"host": "source-postgres",
"generated_id": "b8965c07-7e40-49b4-abad-9dad8b90b3da"
},
{
"name": "Catalogue MySQL",
"database": "catalogue",
"type": "mysql",
"username": "app",
"password": "app-mdp-demo",
"port": 3306,
"host": "source-mysql",
"generated_id": "b85ca400-9764-4d05-b608-fd3be14fb92d"
}
]
}

Quelques champs méritent une explication :

  • type sélectionne le moteur : postgresql, mysql, mariadb, mongodb, redis, sqlite, valkey, mssql, firebird ou docker-volume.
  • host est le nom joignable depuis l'agent. Comme l'agent partage le réseau portabase_network, il atteint les bases par leur nom de conteneur.
  • generated_id est un identifiant stable (un UUID) : il relie la configuration à l'historique côté dashboard. Ne le réutilisez pas d'une base à l'autre.

Rechargez l'agent pour qu'il prenne le fichier en compte :

Fenêtre de terminal
docker compose restart
docker logs portabase-agent | grep "instances loaded"
# INFO Databases: 2 instances loaded

Les deux bases apparaissent alors dans le dashboard, rattachées à votre projet.

Ouvrez une base depuis le projet, par exemple Boutique PostgreSQL. La fiche affiche les compteurs (sauvegardes disponibles, taux de réussite) et deux onglets, Backup et Restoration. Cliquez sur le bouton Backup en haut à droite, puis confirmez.

Une sauvegarde réussie dans la liste des backups

L'agent exécute le dump et l'envoie vers le stockage. Ses journaux tracent chaque étape :

INFO Starting backup for database Boutique PostgreSQL
INFO Running FC backup for Boutique PostgreSQL
INFO Upload completed successfully. Final size: 1099
INFO Database backup job finished

La sauvegarde apparaît dans l'onglet Backup avec son statut success, sa taille et sa durée. C'est le moment de vérifier ce qu'une sauvegarde ne prouve pas : qu'on sait la restaurer.

Une sauvegarde n'a de valeur que si la restauration fonctionne. On le prouve en simulant un incident : on vide la table, on restaure, on vérifie que les données reviennent.

  1. État de départ. La base boutique contient trois clients :

    Fenêtre de terminal
    docker exec source-postgres psql -U app -d boutique -c "SELECT * FROM clients ORDER BY id;"
    # id | nom | ville
    # ----+---------+--------
    # 1 | Dupont | Lyon
    # 2 | Martin | Nantes
    # 3 | Bernard | Lille
  2. Incident simulé. On vide la table, comme le ferait une fausse manipulation :

    Fenêtre de terminal
    docker exec source-postgres psql -U app -d boutique -c "TRUNCATE clients;"
    docker exec source-postgres psql -U app -d boutique -c "SELECT count(*) FROM clients;"
    # 0
  3. Restauration. Dans l'onglet Backup de la base, ouvrez le menu de la sauvegarde (les trois points) et choisissez Restore. Portabase demande le stockage d'où lire la sauvegarde : sélectionnez System (le stockage local), puis Confirm.

    Choix du stockage lors d'une restauration

  4. Vérification. Les trois clients sont de retour :

    Fenêtre de terminal
    docker exec source-postgres psql -U app -d boutique -c "SELECT count(*) FROM clients;"
    # 3

Côté agent, la restauration est tracée et rapide :

INFO Starting restore for database Boutique PostgreSQL
INFO Restore completed successfully for database Boutique PostgreSQL
INFO [RestoreService] Status: success | Duration: 77ms

Historique de restauration réussie dans le dashboard

Le cycle est bouclé : la donnée perdue est revenue depuis une sauvegarde pilotée par le dashboard. C'est cette boucle, testée à froid, qui distingue une vraie stratégie de sauvegarde d'un simple dump qu'on espère valide.

Le lab tourne en local avec des raccourcis à corriger avant tout usage réel :

  • Stockage distant obligatoire. Le stockage local vit sur la même machine que l'agent. Une panne disque emporte les bases et leurs sauvegardes. Configurez un canal S3 ou équivalent pour respecter la règle 3-2-1 (au moins une copie hors site).
  • Comptes de sauvegarde dédiés. Donnez à l'agent un utilisateur en lecture seule par base, jamais le compte root ou postgres.
  • Planification et rétention. Ce guide déclenche les sauvegardes à la main pour la démonstration. En production, définissez une planification et une politique de rétention par base.
  • HTTPS. Exposez le dashboard derrière un reverse proxy avec un certificat, ce qui règle au passage le piège PROJECT_URL.
  • Portabase orchestre, il ne remplace pas pg_dump ni mysqldump : il les appelle et centralise l'historique.
  • L'architecture sépare le dashboard (cerveau, interface) de l'agent (bras, au plus près des bases).
  • Le piège numéro un est PROJECT_URL : elle doit être joignable à l'identique depuis le navigateur et le conteneur, sinon « Failed to fetch ».
  • Les bases se déclarent dans le fichier de configuration de l'agent (type, host, port, identifiants), qui contient des secrets à protéger.
  • Une sauvegarde ne vaut que si la restauration est testée : rejouez la boucle complète régulièrement.
  • En production, stockage distant obligatoire (règle 3-2-1), comptes de sauvegarde dédiés et HTTPS derrière un reverse proxy.

Ces questions condensent les décisions à prendre au démarrage : le positionnement face aux outils natifs, les moteurs pris en charge, le stockage des sauvegardes et le piège réseau de la première installation.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn