Skip to content
pcreative Commerce

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.

VolumeWhat is in itIf you lose it
dbThe database: orders, customers, products, settingsYou have lost everything
datossecretos.env (the JWT_SECRET and COOKIE_SECRET generated on first start) and the publishable keyNew secrets are generated and everything stored encrypted becomes unreadable: gateway keys saved from the panel, digital codes, payout accounts
privadoFiles customers never download directly: digital downloads, customisation uploads, author piecesThose files are gone; the orders still point to them
staticImages uploaded from the panelProduct pages with no photos, and they do not regenerate
temasInstalled themes (the factory ones come back with the image)You reinstall the themes you added
temas-en-marchaBuilt copies of active themesNothing: 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).dump

Outside Docker:

pg_dump -Fc "$DATABASE_URL" > store-$(date +%F).dump

Compressed 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.dump

Backing 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 .
done

datos 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 shop

Under 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.