Backups, and what to do when something falls over
The backend uses only Postgres (there is no Redis) plus a few folders on disk.
What has to be saved
The backend uses only Postgres (there is no Redis) plus a few folders on disk.
Under Docker each one is a volume. The commands on this page are run from the
install folder, where the package's file is called docker-compose.yml; in the
source repository the same file is docker-compose.full.yml, so there add
-f docker-compose.full.yml after docker compose.
| Volume | What is in it | If you lose it |
|---|---|---|
db | The database: orders, customers, products, settings | You have lost everything |
datos | secretos.env (the JWT_SECRET and COOKIE_SECRET generated on first start) and the publishable key | New secrets are generated and everything stored encrypted becomes unreadable: gateway keys saved from the panel, digital codes, payout accounts |
privado | Files customers never download directly: digital downloads, customisation uploads, author pieces | Those files are gone; the orders still point to them |
static | Images uploaded from the panel | Product pages with no photos, and they do not regenerate |
temas | Installed themes (the factory ones come back with the image) | You reinstall the themes you added |
temas-en-marcha | Built copies of active themes | Nothing: reactivating the theme rebuilds it |
db, datos and privado are the essential three. If you set
PCC_CLAVE_CIFRADO (or JWT_SECRET) yourself in .env, keep that value as
carefully as the database: it is the key to the encrypted data.
Outside Docker the same things are the database in DATABASE_URL, your .env,
and the backend's privado/ and static/ folders.
Backing up the database
Under Docker the database publishes no port, so pg_dump from the host cannot
reach it. Run it inside the db container, from the install folder (user and
database are POSTGRES_USER and POSTGRES_DB from .env; defaults comercio
and pcreative_commerce):
docker compose exec -T db \
pg_dump -Fc -U comercio pcreative_commerce > store-$(date +%F).dumpOutside Docker:
pg_dump -Fc "$DATABASE_URL" > store-$(date +%F).dumpCompressed format (-Fc) because it lets you restore individual parts.
Put it in a daily cron and keep it off the server. A copy on the same machine does not protect against the case that happens most: losing the machine.
Restoring:
docker compose exec -T db \
pg_restore --clean --no-owner -U comercio -d pcreative_commerce < store-2026-08-04.dump
# outside Docker
pg_restore --clean --no-owner -d "$DATABASE_URL" store-2026-08-04.dumpBacking up the volumes
Compose prefixes volume names with the project, pcreative-commerce-full. One
tarball per volume:
for v in datos privado static temas; do
docker run --rm -v pcreative-commerce-full_$v:/v:ro -v "$PWD":/copia alpine \
tar czf /copia/$v-$(date +%F).tgz -C /v .
donedatos holds secrets: keep that copy somewhere only you can read.
Before every migration
Any update that touches the database deserves a backup right before, even if you have the daily one. The daily one is from this morning; the migration is from now.
set -a; . ./.env; set +a # pg_dump needs DATABASE_URL in the shell
pg_dump -Fc "$DATABASE_URL" > pre-migration-$(date +%F-%H%M).dump
npm run preparar # applies whatever the schema is missing and prepares the shopUnder Docker there is nothing to run by hand: the backend container runs
preparar every time it starts. Take the dump (with the docker compose … exec
command above) before docker compose up -d --build.
preparar takes a lock in Postgres: two servers starting at once do not step
on each other applying the same migration. And it deletes nothing: if something
fails, the database stays as it was and the backup is still there just in case.
Images live outside the code
Photos uploaded from the panel go to the static/ folder, which in the
container is a separate volume. On purpose: if they lived next to the code,
every update would wipe them. When you back up, back up that volume too — the
database stores the URLs, not the images.
Watching that it is still up
A backend that falls over at 4am does not report itself. A checker every few minutes that looks at whether the store responds and restarts it if not costs ten lines and saves the customer's phone call asking why their site has been erroring for hours.
Two things that help if you use pm2:
- Growing wait between restarts. If the database restarts — the system's automatic updates do it overnight — the backend goes down and, without a wait, relaunches in a loop dozens of times per incident.
- Log rotation, or the file grows without limit until it fills the disk.
What almost nobody checks until it hurts
- Does the backup actually restore? A backup that has never been tested is not a backup, it is a file.
- Is it off the server?
- How much data are you willing to lose? That decides whether the backup is daily or hourly.