Lifecycle scripts for Bookmark Manager. All scripts must be run from the repo root.
./scripts/<script>.sh [args]Every script uses set -euo pipefail and colour-coded output:
- Cyan — informational step
- Green — success
- Yellow — warning / action required
- Red — error (written to stderr)
- Quick reference
- install.sh
- uninstall.sh
- rebuild.sh
- start.sh
- stop.sh
- restart.sh
- logs.sh
- status.sh
- dev.sh
- backup.sh
| Script | Args | Description |
|---|---|---|
install.sh |
— | Full first-time install |
uninstall.sh |
— | Remove services and optionally data |
rebuild.sh |
— | Rebuild API image and restart service |
start.sh |
— | Start the pod |
stop.sh |
— | Stop the pod |
restart.sh |
[api|db|pma] |
Restart one service or the whole pod |
logs.sh |
[api|db|pma|all] |
Tail journalctl logs |
status.sh |
— | Show systemctl status for all services |
dev.sh |
— | Run API locally via Bun (no container) |
backup.sh |
— | Dump DB to backups/bookmark_YYYY-MM-DD_HHMMSS.sql.gz |
Full first-time install. Run this once after cloning the repo.
./scripts/install.shSteps executed:
- Verifies
api/andquadlet/directories exist (confirms repo root). - Checks for
api/.env.- If missing: copies
api/.env.example→api/.env, prints a warning, and exits so you can fill in real passwords before continuing.
- If missing: copies
- Verifies
podmanis onPATH. - Pulls base images:
docker.io/mariadb:11anddocker.io/phpmyadmin:5. - Builds
localhost/bookmark-api:latestfromapi/Dockerfile. - Creates the DB data volume directory:
~/.local/share/bookmark-manager/prod-db. - Copies
quadlet/*.{pod,container}→~/.config/containers/systemd/. - Runs
systemctl --user daemon-reload. - Runs
systemctl --user enable --now bookmark-pod.service. - Prints service URLs.
First-time flow:
# 1. Copy and edit credentials
cp api/.env.example api/.env
nano api/.env # set DB_PASSWORD, MARIADB_PASSWORD, MARIADB_ROOT_PASSWORD
# 2. Install
./scripts/install.shRe-running install is safe — it is idempotent. Existing Quadlet files are overwritten, the image is rebuilt, and the pod is (re)started.
Outputs on success:
API http://localhost:11650
Swagger UI http://localhost:11650/docs
phpMyAdmin http://localhost:11651 (login required)
Stops and removes all Bookmark Manager services. Interactively asks before removing the container image or the database data.
./scripts/uninstall.shSteps executed:
- Stops
bookmark-pod.service(tolerates already-stopped). - Disables
bookmark-pod.service(tolerates not-enabled). - Removes the four Quadlet unit files from
~/.config/containers/systemd/. - Runs
systemctl --user daemon-reload. - Asks: remove
localhost/bookmark-api:latestimage? (default: N) - Asks: delete DB data volume at
~/.local/share/bookmark-manager/prod-db? (default: N, with a prominent warning)
Data safety: The DB volume is never deleted without an explicit
yconfirmation. This preserves all bookmark data across uninstall/reinstall cycles.
Rebuilds the API container image from source and restarts only the API service. Use this after making changes to api/src/.
./scripts/rebuild.shSteps executed:
- Builds
localhost/bookmark-api:latestfromapi/Dockerfile. - Restarts
bookmark-api.service(DB and phpMyAdmin are unaffected). - Polls
GET /healthevery 2 seconds for up to 30 seconds.- Exits 0 when healthy, exits 1 with instructions if the timeout is exceeded.
Workflow for source changes:
# Edit API source
nano api/src/server.ts
# Rebuild and restart
./scripts/rebuild.sh
# Tail logs if something looks wrong
./scripts/logs.sh apiWorkflow for schema changes (generate migration first):
cd api && bun run db:generate
cd ..
./scripts/rebuild.sh # migrations run automatically on container startStarts the pod and all three containers (MariaDB, phpMyAdmin, API).
./scripts/start.shEquivalent to systemctl --user start bookmark-pod.service. Prints the service URLs on success.
Stops the pod and all three containers gracefully.
./scripts/stop.shEquivalent to systemctl --user stop bookmark-pod.service. DB data is preserved.
Restarts one specific service or the entire pod.
./scripts/restart.sh # restart whole pod (all containers)
./scripts/restart.sh api # restart bookmark-api.service only
./scripts/restart.sh db # restart bookmark-db.service only
./scripts/restart.sh pma # restart bookmark-pma.service only| Argument | Service restarted |
|---|---|
| (none) | bookmark-pod.service (all containers) |
api |
bookmark-api.service |
db |
bookmark-db.service |
pma |
bookmark-pma.service |
Tip: after a
rebuild.shthe API is already restarted automatically. Userestart.sh apionly when you need to restart without rebuilding the image.
Tails journalctl --user logs for one or all services. Exits on Ctrl+C.
./scripts/logs.sh # API logs (default)
./scripts/logs.sh api # bookmark-api logs
./scripts/logs.sh db # bookmark-db logs
./scripts/logs.sh pma # bookmark-pma logs
./scripts/logs.sh all # all three services interleaved| Argument | Logs shown |
|---|---|
(none) / api |
bookmark-api |
db |
bookmark-db |
pma |
bookmark-pma |
all |
bookmark-api + bookmark-db + bookmark-pma |
Shows systemctl --user status for the pod and all three container services in one view.
./scripts/status.shEquivalent to:
systemctl --user status bookmark-pod.service \
bookmark-api.service \
bookmark-db.service \
bookmark-pma.service \
--no-pager -lDoes not exit non-zero when services are inactive — safe to run at any time.
Runs the API locally using Bun in watch mode, without any container. Useful for rapid development when the MariaDB container is already running separately.
./scripts/dev.shSteps executed:
- Checks for
api/.env.- If missing: copies from
api/.env.exampleand exits with a prompt to fill in passwords.
- If missing: copies from
- Verifies
bunis onPATH. - Runs
bun installinapi/ifnode_modules/is absent orpackage.jsonis newer. - Executes
bun run dev(bun --watch src/server.ts) — reloads on file changes.
Prerequisites: A running MariaDB instance reachable at the host/port configured in api/.env (DB_HOST / DB_PORT). The DB container can be started without the API via:
./scripts/start.sh # start everything, then...
systemctl --user stop bookmark-api.service # stop only the API container
./scripts/dev.sh # run API locally against the container DBDumps the live MariaDB database to a gzip-compressed SQL file in backups/.
./scripts/backup.sh
# → backups/bookmark_2026-02-25_143022.sql.gz (42K)What it does:
- Reads
DB_USER,DB_PASSWORD,DB_NAMEfromapi/.env. - Verifies the
bookmark-dbcontainer is running. - Runs
mariadb-dump --single-transaction --routines --triggersinside the container. - Pipes the output through
gzip -9and saves tobackups/bookmark_YYYY-MM-DD_HHMMSS.sql.gz.
Notes:
backups/is gitignored — dump files are never committed.- A backup is also available via the browser at
GET /backup?token=<BACKUP_TOKEN>(setBACKUP_TOKENinapi/.env). - To restore:
gunzip -c backups/<file>.sql.gz | podman exec -i bookmark-db mariadb -u<user> -p<pass> <dbname>
© 2026 Jaco Steyn — Licensed under CC BY-SA 4.0