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_KEY to at least 16 characters.
  • Set URL to the address the browser will actually use.
  • Make DB_PASSWORD and MYSQL_PASSWORD identical.
  • Give MYSQL_ROOT_PASSWORD its 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:

  1. Stop the stack with docker compose down.
  2. Move the original storage directory to its new location, preserving subdirectories such as /zim and /models.
  3. Change the host side of the admin bind mount.
  4. Set NOMAD_STORAGE_PATH to that exact same host path.
  5. Change the host side of the disk-collector mount to match.
  6. 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