# 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-.sql.gz and ...-files-.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:' ``` 2. Pull and reprovision: ```bash podman pull docker.io/espocrm/espocrm: ./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: ./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 ```