In rootless Docker, uid=0 in-container = cldrzd on host (not privileged). Pinning to uid=1000 in-container mapped to host uid=100999 (phantom UID), which cannot write to the cldrzd-owned data directory. The Dockerfile USER directive is overridden by compose user: "0" anyway, so revert to a standard non-root app user without explicit uid/gid. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
demoapp — Clouderized Demo App
A task manager built with Scala 3 + Thorium, used to demonstrate Clouderized's core value prop: stateful app upgrades with zero data loss.
Tasks are persisted in SQLite backed by a Docker named volume (demouser-data). Rebuilding or upgrading the container leaves all user data intact.
Prerequisites
- Access to
git.clouderized.com(Gitea) - The app deployed at
demouser.clouderized.com - VPS SSH access:
ssh -i ssh/id_rsa -p 9876 cldrzd@194.233.75.123
v1 → v2 Upgrade Demo (with data persistence)
This is the primary demo sequence. It shows that Clouderized can upgrade a running app without wiping user data.
1. Deploy v1.0.0
On the VPS:
cd ~/customers/demouser/demoapp && git fetch --tags && git checkout v1.0.0
cd ~/customers/demouser && docker compose up -d --build
Visit https://demouser.clouderized.com:
- Teal header, version badge shows v1.0.0
- Three pre-seeded tasks are present
/healthreturns{"status":"ok"}
2. Add user data
In the browser, add 2–3 tasks and delete one of the pre-seeded tasks. This simulates real user activity before an upgrade.
3. Upgrade to v2.0.0
On the VPS:
cd ~/customers/demouser/demoapp && git checkout v2.0.0
cd ~/customers/demouser && docker compose up -d --build
Revisit https://demouser.clouderized.com:
- Blue header, version badge shows v2.0.0
- All tasks added in step 2 are still there
- Pre-seeded tasks are not re-added (seeding is skipped when the table is non-empty)
4. Verify persistence
# On the VPS — confirm the volume exists and is mounted
docker volume inspect demouser-data
How Persistence Works
| Component | Detail |
|---|---|
| Storage | SQLite at /data/tasks.db inside the container |
| Volume | Docker named volume demouser-data mounted at /data |
| Env var | DB_PATH=/data/tasks.db (set in docker-compose.yml) |
| Seeding | Pre-seed runs only when the tasks table is empty |
The named volume demouser-data survives docker compose up --build — data is never lost on rebuild.
Config Reference
All visual changes are driven by src/main/resources/site.conf:
| Key | v1.0.0 | v2.0.0 | What it controls |
|---|---|---|---|
app.version |
"1.0.0" |
"2.0.0" |
Version badge in header |
app.theme.background |
"#0F766E" (teal) |
"#1D4ED8" (blue) |
Header and button color |
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | / |
Task list (HTML) |
| GET | /health |
Health check (JSON) |
| POST | /tasks |
Create task (form) |
| POST | /tasks/:id/toggle |
Toggle completion |
| POST | /tasks/:id/delete |
Delete task |
Notes
- The app runs on the Pro tier (2GB RAM, 2 vCPU) since JVM needs the headroom.
- Local dev uses
./tasks.db(relative path) whenDB_PATHis not set.