This is a third-party deployment note, not official Project NOMAD or Crosstalk Solutions documentation. It focuses on manual Compose installation and a storage-migration pitfall that can make an existing content library appear empty.
Project NOMAD is an offline-first knowledge and education server for material such as Wikipedia, books, courses, and maps. Installation and content downloads need connectivity; downloaded content can then remain available without public internet access.
Start with the supported deployment shape
The guide’s main path is an x86_64 Debian/Ubuntu host. An installation script is available, while an existing Docker host can use the supplied management_compose.yaml template manually.
For the manual route, keep docker-compose.yml in /opt/project-nomad. The updater mounts that directory to modify image references. Replace every replaceme placeholder before startup:
- Set
APP_KEYto at least 16 characters. - Set
URLto the address the browser will actually use. - Make
DB_PASSWORDandMYSQL_PASSWORDidentical. - Give
MYSQL_ROOT_PASSWORDits own strong value.
NOMAD has no authentication by default. Keep the management interface, log viewer, and application ports off the public internet. Several services mount the Docker socket, so access to the management interface carries significant control over the host’s containers.
The pitfall: changing only one storage reference
The default content directory appears in three places. These are excerpts from separate parts of the Compose template, not one block to paste together:
Admin service volume:
- /opt/project-nomad/storage:/app/storage
Admin environment variable:
- NOMAD_STORAGE_PATH=/opt/project-nomad/storage
Disk-collector volume:
- /opt/project-nomad/storage:/storage
All three host paths must match exactly, including letter case. The container destinations differ intentionally; the host-side directory is what must stay consistent.
For example, /mnt/Data and /mnt/data are different paths. A typo can cause Docker to create a new empty directory. The interface then shows an empty library, making content look as though it “disappeared” after the move. Pointing the deployment at an empty directory does not make it discover the previous files automatically.
Fix the move as a complete operation
Follow the migration sequence rather than changing a mount while the stack is running:
- Stop the stack with
docker compose down. - Move the original storage directory to its new location, preserving subdirectories such as
/zimand/models. - Change the host side of the admin bind mount.
- Set
NOMAD_STORAGE_PATHto that exact same host path. - Change the host side of the disk-collector mount to match.
- Start with
docker compose up -d, then check both content and disk usage in the interface.
If the library is empty, stop again and compare all three references with the actual directory containing the original files. For external or network storage, mount it on the host first; NOMAD does not configure the storage protocol itself. MySQL and Redis have separate data directories, so the content-storage setting is not a replacement for those mounts.
A small port-mapping check
If host port 8080 is occupied, change only the left side of the mapping and update URL:
- "9090:8080"
Leave the container’s PORT=8080 unchanged.
Finally, Docker availability alone does not make every NAS a supported target. NAS graphical stack imports are outside the documented support path, and Raspberry Pi/ARM was not officially tested or supplied with official images at the guide’s check time.
The full Chinese step-by-step guide (install script or manual Compose, NAS / Raspberry Pi boundaries, troubleshooting) is here: https://9012938178391.gumroad.com/l/tjlxtb
Top comments (1)
Path consistency deserves a machine-checkable invariant, not only deployment documentation. A startup validation that compares the three resolved paths and refuses to mutate data on mismatch can prevent a configuration drift from becoming a split-brain recovery problem.