Add README, customization snapshot, and snapshot/restore tooling

Provisioning now restores all GUI customizations on reset+reprovision:

- create_pod_espocrm.sh: deploy the version-controlled espocrm-custom/ tree
  (CTag entity, layouts, i18n, clientDefs, custom views, custom CSS) into the
  pod, then chown www-data and rebuild. Replaces the earlier inline CSS-only
  step. Adds a live-phase cache rebuild so customizations and the client
  cacheTimestamp are refreshed on every run.
- espocrm-custom/: snapshot of custom/ and client/custom/ (source of truth).
- snapshot_espocrm_custom.sh: refresh the snapshot from a running pod.
- readme.md: usage, first-time host setup, image-update and reset workflows.
- Include the task/instruction notes and plan.md for reference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
mkt
2026-06-06 16:51:02 +02:00
parent 79ea236da0
commit cebe829dcd
79 changed files with 2064 additions and 45 deletions

276
readme.md
View File

@@ -1 +1,275 @@
user mkt folder /home/mkt/bin git repository
# EspoCRM on DesTEngSsv006 — rootless Podman pod
This repository contains the shell scripts that run **EspoCRM** as a **rootless
Podman pod** under the local user `mkt`, plus the version-controlled snapshot of
all GUI customizations. Everything is driven by a handful of `*.sh` scripts; you
do not edit anything inside the running container by hand.
> This is a **private** repository classified as a company secret. Plaintext
> passwords live directly in `create_pod_espocrm.sh` on purpose — keep the repo
> private.
---
## 1. What gets deployed
`create_pod_espocrm.sh` builds one pod with three containers:
| Container | Image | Role |
|-----------------------|-----------------------------------------|---------------------------------------|
| `mariadb_ctr` | `docker.io/library/mariadb:11.4.12` | Database (utf8mb4 / utf8mb4_unicode_ci) |
| `espocrm_ctr` | `docker.io/espocrm/espocrm:9.3.8` | Web app (Apache) |
| `espocrm_daemon_ctr` | `docker.io/espocrm/espocrm:9.3.8` | Scheduler / cron loop |
Pod name: **`espocrm_pod`**. Images are **pinned** (no auto-updates).
**Ports — loopback only** (reach them via an SSH tunnel or a local reverse proxy):
| Host address | → | Container | Purpose |
|-----------------------|----|-----------|-------------------------------|
| `127.0.0.1:8093` | → | `:80` | EspoCRM web UI |
| `127.0.0.1:8094` | → | `:3306` | MariaDB (host-side DB access) |
Web UI: **http://127.0.0.1:8093** · Admin user: **`admin`** (password = the
`ESPOCRM_ADMIN_PASSWORD` value in `create_pod_espocrm.sh`).
**Data lives in bind mounts on the host** (not in named volumes):
```
~/.local/share/espocrm_pod/mariadb -> /var/lib/mysql (database files)
~/.local/share/espocrm_pod/espocrm -> /var/www/html (app, uploads, config, customizations)
```
**Autostart:** Podman 4.3.1 has no Quadlet, so the scripts generate
`systemd --user` units (`podman generate systemd --files --new`) and enable them.
The systemd-managed instance is what actually runs after provisioning.
---
## 2. The scripts at a glance
| Script | What it does |
|------------------------------|-----------------------------------------------------------------------------|
| `create_pod_espocrm.sh` | Create/recreate the pod, deploy GUI customizations, rebuild, enable autostart. **Safe to re-run.** |
| `stop_pod_espocrm.sh` | Stop the running containers + pod (autostart stays enabled). |
| `backup_espocrm.sh` | Write a timestamped DB dump + a tarball of the app data dir to `~/bak`. |
| `restore_espocrm.sh` | Restore a backup set (DB + files). `--list` shows sets, `--yes` skips the prompt. |
| `snapshot_espocrm_custom.sh` | Refresh the `espocrm-custom/` snapshot from the running pod after GUI edits. |
| `reset_pod_espocrm.sh` | **DANGER:** delete the pod, units **and all data**. Requires typing a phrase.|
| `create_pod_traefik.sh` | Unrelated reference script (Traefik pod) — kept as the house-style template. |
All scripts are run **as user `mkt`**, from this directory:
```bash
cd ~/bin
./create_pod_espocrm.sh
```
---
## 3. First-time setup on a freshly installed host
The scripts assume the rootless-Podman groundwork is already in place. On a brand
new host, verify these **prerequisites** first (one-time, usually needs root):
1. **Podman installed** (4.3.x is what this was built for):
```bash
podman --version
```
2. **User `mkt` exists** and has **subuid/subgid ranges** (needed for rootless
user-namespace mapping):
```bash
grep '^mkt:' /etc/subuid /etc/subgid # each should print a line
# if missing (as root): usermod --add-subuids 524288-589823 --add-subgids 524288-589823 mkt
```
3. **Linger enabled** for `mkt`, so the user's systemd services start at boot and
keep running without an active login:
```bash
loginctl show-user mkt -p Linger # want: Linger=yes
# if not: sudo loginctl enable-linger mkt
```
4. **A user systemd session is reachable.** When working over plain SSH you may
need:
```bash
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemctl --user status # should respond
```
Then, as `mkt`:
```bash
cd ~/bin
./create_pod_espocrm.sh
```
On this **first run** the script will:
- pull the pinned images,
- run the EspoCRM installer against an empty database (creates the schema and the
`admin` user from the credentials in the script),
- deploy all GUI customizations from `espocrm-custom/`,
- run a cache rebuild,
- generate and enable the `systemd --user` services (autostart).
When it finishes, open **http://127.0.0.1:8093** and log in as `admin`.
> Reaching the UI from your workstation: it only listens on loopback. Use an SSH
> tunnel, e.g. `ssh -L 8093:127.0.0.1:8093 mkt@DesTEngSsv006`, then browse to
> `http://127.0.0.1:8093` locally.
---
## 4. Everyday operations
```bash
# Start (after a stop) — the systemd units stay enabled, so this also happens on boot:
systemctl --user start pod-espocrm_pod.service \
container-mariadb_ctr.service container-espocrm_ctr.service container-espocrm_daemon_ctr.service
# Stop everything (autostart remains enabled):
./stop_pod_espocrm.sh
# Status:
systemctl --user status pod-espocrm_pod.service
systemctl --user status container-espocrm_ctr.service
# Live logs:
journalctl --user -u container-espocrm_ctr.service -f
```
Re-running `./create_pod_espocrm.sh` at any time is safe: it recreates the
containers from the persisted data, redeploys customizations, and rebuilds.
---
## 5. Backups and restore
Backups are **manual** (no timer/cron). Each run writes two timestamped files to
`~/bak`: a gzipped SQL dump and a tarball of the app data dir (which includes
`config.php` with the crypt key, uploads, and customizations).
```bash
./backup_espocrm.sh # creates ~/bak/espocrm-db-<TS>.sql.gz and ...-files-<TS>.tar.gz
./restore_espocrm.sh --list # list available backup sets
./restore_espocrm.sh # restore the latest set (asks for confirmation)
./restore_espocrm.sh 20260606-1346 # restore a specific set by timestamp
```
`restore_espocrm.sh` checks the archives, drops & re-imports the database, and
replaces the app data dir, then rebuilds. **Take a backup before any risky
operation** (image update, reset, major change).
---
## 6. GUI customizations (important workflow)
All customizations made through the EspoCRM GUI (Entity Manager, Layout Manager,
Label Manager, custom fields/views, custom CSS, …) are stored in
**`espocrm-custom/`** in this repo and deployed by `create_pod_espocrm.sh`. This
snapshot is the **single source of truth** — a reset + reprovision restores it.
Current customizations captured here include the `CTag` tag entity and its
relationships, the Tags panels (Create `+` button removed, row menu reduced to
*Unlink*), the signature-editor image button, and variable-width Markdown tables.
**The golden rule:** after you change anything in the EspoCRM GUI, capture it:
```bash
./snapshot_espocrm_custom.sh # pull current customizations into espocrm-custom/
git add espocrm-custom && git commit -m "Update EspoCRM customizations snapshot"
```
If you skip this, the next `create_pod_espocrm.sh` run will **revert** your
un-captured GUI changes back to whatever is in `espocrm-custom/`.
> Note: customizations are *files*. Your actual records (tags, opportunities,
> contacts, …) are *data* and live only in the database — protect those with
> `backup_espocrm.sh`, not the snapshot.
---
## 7. Updating the container images
Images are pinned, so updates are deliberate. **Always back up first.**
```bash
./backup_espocrm.sh
```
### EspoCRM (e.g. 9.3.8 → a newer 9.x)
1. Edit the version in `create_pod_espocrm.sh` (the web and daemon share the same
`ESPO_IMAGE` variable, so one change covers both):
```bash
ESPO_IMAGE='docker.io/espocrm/espocrm:<new-version>'
```
2. Pull and reprovision:
```bash
podman pull docker.io/espocrm/espocrm:<new-version>
./create_pod_espocrm.sh
```
The EspoCRM container entrypoint **auto-runs its upgrade** when the image
version is newer than the installed version (it migrates the database and app
files on the bind mount). The script then redeploys customizations and
rebuilds.
3. Verify the UI and your customizations. If you tweaked anything in the GUI as
part of the upgrade, re-run `./snapshot_espocrm_custom.sh` and commit.
### MariaDB (e.g. 11.4.12 → a newer 11.4.x patch)
Patch/minor updates **within the same 11.4 LTS series** are data-compatible:
```bash
# edit DB_IMAGE in create_pod_espocrm.sh, then:
podman pull docker.io/library/mariadb:<new-11.4.x>
./create_pod_espocrm.sh
```
For a **major** MariaDB jump (e.g. 11.x → 12.x) do not just swap the tag —
back up, then dump/restore into a fresh data dir, and test. When in doubt, take
a backup and try it on a throwaway copy first.
### After any image change
```bash
podman ps # all three containers Up
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8093/ # expect 200
podman image prune # optionally drop the old images
```
Commit the bumped image tag(s) in `create_pod_espocrm.sh`.
---
## 8. Resetting / uninstalling
`reset_pod_espocrm.sh` **destroys the pod, the systemd units, and ALL data**
(database + uploads). It asks you to type `RESET espocrm_pod` to proceed.
```bash
./backup_espocrm.sh # do this first if you might want the data back
./reset_pod_espocrm.sh
./create_pod_espocrm.sh # fresh install; customizations are restored from espocrm-custom/
# (optional) restore your data:
./restore_espocrm.sh
```
After a reset, a fresh `create_pod_espocrm.sh` gives you a clean install with all
**customizations** back in place; **records** come back only via `restore`.
---
## 9. Repository layout
```
create_pod_espocrm.sh # provision + autostart + deploy customizations
stop_pod_espocrm.sh # stop the pod
backup_espocrm.sh # back up DB + files to ~/bak
restore_espocrm.sh # restore a backup set
snapshot_espocrm_custom.sh # refresh espocrm-custom/ from the running pod
reset_pod_espocrm.sh # DANGER: delete pod + units + all data
espocrm-custom/ # version-controlled GUI customizations (source of truth)
create_pod_traefik.sh # unrelated reference script (house-style template)
*.md # task/instruction notes and this readme
```