GeoIntel release gates / Compile, test, contracts and builds (push) Canceled after 0s
GeoIntel release gates / Python and npm vulnerability policy (push) Canceled after 0s
GeoIntel release gates / GIS image, SBOM and container scan (push) Canceled after 0s
GeoIntel release gates / AI image, SBOM and container scan (push) Canceled after 0s
358 lines
14 KiB
Markdown
358 lines
14 KiB
Markdown
# GeoIntel Unraid all-in-one container
|
|
|
|
GeoIntel can run on Unraid as one DockerMan-native container.
|
|
|
|
Inside that single container:
|
|
|
|
- embedded PostGIS stores the application database
|
|
- Alembic migrations run at startup
|
|
- FastAPI runs on internal `127.0.0.1:8000`
|
|
- nginx serves the React/MapLibre frontend on container port `80`
|
|
- nginx proxies `/api` and `/health` to the internal backend
|
|
|
|
The browser entrypoint is:
|
|
|
|
```text
|
|
http://<unraid-ip>:${GEOINTEL_FRONTEND_PORT}
|
|
```
|
|
|
|
The app icons are served from the same container:
|
|
|
|
```text
|
|
http://<unraid-ip>:${GEOINTEL_FRONTEND_PORT}/geointel-icon.svg
|
|
http://<unraid-ip>:${GEOINTEL_FRONTEND_PORT}/geointel-icon.png
|
|
```
|
|
|
|
## Files
|
|
|
|
- `docker-compose.unraid.yml`: config validation reference for the all-in-one image.
|
|
- `deploy/unraid/Dockerfile.all-in-one`: builds the single container.
|
|
- `deploy/unraid/all-in-one-start.sh`: starts embedded PostGIS, backend and nginx.
|
|
- `deploy/unraid/run-dockerman-container.sh`: starts/replaces the running container with DockerMan labels and editable Unraid metadata.
|
|
- `deploy/unraid/nginx-all-in-one.conf`: frontend and API proxy config for one container.
|
|
- `deploy/unraid/geointel.env.example`: copy to `.env` and edit ports/paths.
|
|
- `deploy/unraid/geointel-unraid-template.xml`: Unraid/DockerMan metadata for editable fields.
|
|
- `deploy/unraid/geointel-icon.svg`: frontend favicon source.
|
|
- `deploy/unraid/geointel-icon.png`: DockerMan/Unraid icon source.
|
|
- `frontend/public/geointel-icon.svg`: frontend-served SVG icon.
|
|
- `frontend/public/geointel-icon-180.png`: frontend PNG icon, also served at
|
|
the stable `/geointel-icon.png` compatibility URL by nginx.
|
|
|
|
The Tower deploy scripts also copy the editable DockerMan template to:
|
|
|
|
```text
|
|
/boot/config/plugins/dockerMan/templates-user/my-geointel.xml
|
|
```
|
|
|
|
and copy the PNG icon to:
|
|
|
|
```text
|
|
/boot/config/plugins/dockerMan/images/geointel-icon.png
|
|
```
|
|
|
|
The template name is `geointel` so it matches the running all-in-one container name. If the Unraid Docker page was already open, refresh it after deploy so DockerMan reloads the template/icon metadata.
|
|
|
|
`docker-compose.unraid.yml` also applies DockerMan labels to the running container:
|
|
|
|
```text
|
|
net.unraid.docker.managed=dockerman
|
|
net.unraid.docker.webui=http://[IP]:[PORT:80]/
|
|
net.unraid.docker.icon=/boot/config/plugins/dockerMan/images/geointel-icon.png
|
|
```
|
|
|
|
These labels are required because a plain Compose container can run correctly while still missing the normal Unraid edit/icon controls.
|
|
|
|
## First setup from the repo
|
|
|
|
From the Unraid shell:
|
|
|
|
```bash
|
|
cd /mnt/user/appdata
|
|
git clone gitea-widefrog:NuklearRabbit/geointel.git geointel
|
|
cd /mnt/user/appdata/geointel
|
|
cp deploy/unraid/geointel.env.example .env
|
|
nano .env
|
|
docker compose -f docker-compose.unraid.yml config
|
|
bash deploy/unraid/deploy-release.sh
|
|
```
|
|
|
|
Set `GEOINTEL_POSTGRES_PASSWORD` to a unique value before that first start.
|
|
|
|
For a browser login, set `GEOINTEL_AUTH_ENABLED=true`, configure one exact
|
|
`GEOINTEL_AUTH_USERNAME`, a `pbkdf2_sha256` password hash and an independent
|
|
random `GEOINTEL_AUTH_SESSION_SECRET` of at least 32 characters. The plaintext
|
|
password is never stored in the repository or container configuration. Browser
|
|
API calls require the signed HttpOnly session cookie; direct loopback calls to
|
|
the backend remain available to trusted in-container operator scripts.
|
|
Production startup fails before replacing the active container when the
|
|
password is empty or one of the documented defaults.
|
|
|
|
Guest access is disabled by default and must be explicitly enabled only for a dedicated demo instance:
|
|
|
|
```env
|
|
GEOINTEL_GUEST_ACCESS_ENABLED=false
|
|
GEOINTEL_GUEST_DISPLAY_NAME=Gast
|
|
GEOINTEL_GUEST_SESSION_TTL_SECONDS=7200
|
|
```
|
|
|
|
Set `GEOINTEL_GUEST_ACCESS_ENABLED=true` only on a dedicated demo deployment
|
|
to enable the guest button and guest endpoint.
|
|
This adds **Als gast verkennen** to the landing page. The generated guest cookie
|
|
is short-lived, project-scoped and limited to the canonical demo workflow.
|
|
Operator mutations and access to another project are rejected by the backend,
|
|
and the frontend hides management and task-starting controls. The mechanism is
|
|
not tenant isolation: never enable it on an instance that contains private,
|
|
customer or operational data. Deploy a separate demo container and storage
|
|
root for public or recruiter-facing access.
|
|
|
|
The repository deploy scripts run the same flow automatically. They validate
|
|
the Compose reference, preserve the current image as
|
|
`geointel-all-in-one:previous`, build an immutable `<commit-sha>-ai` or
|
|
`<commit-sha>-gis` tag plus `latest`, install the DockerMan metadata and start
|
|
the immutable image. An existing matching tag is reused, never rebuilt. A
|
|
failed start, live migration smoke or browser/API smoke automatically attempts
|
|
the previous image without changing the configured PostGIS or storage paths.
|
|
|
|
`scripts/deploy_tower.sh` and `scripts/deploy_tower.ps1` source the remote
|
|
`.env` before building the image. That means `GEOINTEL_INSTALL_AI=true` in
|
|
`/mnt/user/appdata/geointel/.env` is enough for the automatic deploy to build
|
|
the AI-enabled image. Set `GEOINTEL_INSTALL_AI` in the local shell or pass
|
|
`-InstallAi true/false` to the PowerShell wrapper only when you intentionally
|
|
want to override the remote `.env` for that deploy.
|
|
|
|
Database credentials are runtime configuration, not image metadata. The
|
|
all-in-one image does not bake `GEOINTEL_POSTGRES_PASSWORD` into the Dockerfile;
|
|
set it through `.env`, the Unraid template or `docker run -e`.
|
|
|
|
AI dependencies are opt-in. Leave `GEOINTEL_INSTALL_AI=false` for the default
|
|
GIS-only image. Set `GEOINTEL_INSTALL_AI=true`, mount models through
|
|
`GEOINTEL_MODELS_PATH` and configure `YOLO_ENABLED=true` plus
|
|
`YOLO_MODELS_DIR=/app/models` and `YOLO_MODEL_PATH=/app/models/<model>.pt` only
|
|
when you have a local model file.
|
|
The AI-enabled image installs PyTorch/Ultralytics plus the native OpenCV runtime
|
|
libraries needed for Ultralytics imports; it still never downloads model weights.
|
|
The documented CPU runtime installs pinned `torch==2.13.0` and
|
|
`torchvision==0.28.0` from `https://download.pytorch.org/whl/cpu`, avoiding the
|
|
unused CUDA runtime wheels included by the general Linux package index. The
|
|
Dockerfile copies dependency metadata before backend source and applies
|
|
commit/build metadata only after the heavy file layers. Normal code-only
|
|
redeploys therefore reuse the apt, GIS and optional PyTorch dependency layers.
|
|
`YOLO_CONFIG_DIR` defaults to `/app/storage/ultralytics`, a writable persistent
|
|
path, so Ultralytics settings do not fall back to root user config directories.
|
|
|
|
To safely configure an existing local YOLO model on Tower, place one supported
|
|
model file (`.pt`, `.onnx` or `.engine`) under:
|
|
|
|
```text
|
|
/mnt/user/appdata/geointel/models
|
|
```
|
|
|
|
Then run a dry-run first:
|
|
|
|
```bash
|
|
cd /mnt/user/appdata/geointel
|
|
python scripts/configure_yolo_model.py \
|
|
--models-dir /mnt/user/appdata/geointel/models \
|
|
--env-file .env
|
|
```
|
|
|
|
Apply only after the selected host and container paths are correct:
|
|
|
|
```bash
|
|
python scripts/configure_yolo_model.py \
|
|
--models-dir /mnt/user/appdata/geointel/models \
|
|
--env-file .env \
|
|
--apply
|
|
bash deploy/unraid/run-dockerman-container.sh
|
|
```
|
|
|
|
If multiple model files are present, add `--model-file /mnt/user/appdata/geointel/models/<name>.pt`.
|
|
The helper does not download weights, does not load a model and does not run
|
|
inference; it only updates the env file for the mounted local model.
|
|
|
|
Validate liveness, dependency readiness and the canonical API:
|
|
|
|
```bash
|
|
curl -fsS "http://192.168.10.150:${GEOINTEL_FRONTEND_PORT:-1202}/health/live"
|
|
curl -fsS "http://192.168.10.150:${GEOINTEL_FRONTEND_PORT:-1202}/health/ready"
|
|
curl -fsS "http://192.168.10.150:${GEOINTEL_FRONTEND_PORT:-1202}/api/v1/system/capabilities"
|
|
curl -fsS "http://192.168.10.150:${GEOINTEL_FRONTEND_PORT:-1202}/api/v1/projects"
|
|
curl -I "http://192.168.10.150:${GEOINTEL_FRONTEND_PORT:-1202}/geointel-icon.svg"
|
|
curl -I "http://192.168.10.150:${GEOINTEL_FRONTEND_PORT:-1202}/geointel-icon.png"
|
|
```
|
|
|
|
The live migration smoke also checks PostgreSQL database collation metadata.
|
|
When reusing a PostGIS volume created by an older Debian/glibc runtime, it may
|
|
print `COLLATION_VERSION_MISMATCH`. This is a maintenance warning, not an app
|
|
startup failure. Review backups first, then acknowledge the new runtime
|
|
collation version inside the running container:
|
|
|
|
```bash
|
|
docker exec -it geointel psql -U "${GEOINTEL_POSTGRES_USER:-geointel}" -d "${GEOINTEL_POSTGRES_DB:-geointel}"
|
|
ALTER DATABASE "geointel" REFRESH COLLATION VERSION;
|
|
```
|
|
|
|
If you rely on text indexes with locale-specific ordering, plan a maintenance
|
|
window and rebuild the affected indexes before acknowledging the version. The
|
|
current GeoIntel V1 spatial workflows primarily use UUIDs, JSON metadata and
|
|
PostGIS geometry indexes, but the warning should still be tracked explicitly.
|
|
|
|
## Change the browser port
|
|
|
|
Edit `.env`:
|
|
|
|
```env
|
|
GEOINTEL_FRONTEND_PORT=1203
|
|
GEOINTEL_CORS_ORIGINS=http://localhost:1203,http://127.0.0.1:1203,http://192.168.10.150:1203
|
|
```
|
|
|
|
Apply:
|
|
|
|
```bash
|
|
bash deploy/unraid/deploy-release.sh
|
|
```
|
|
|
|
## Persistent paths
|
|
|
|
Recommended Unraid paths:
|
|
|
|
```env
|
|
GEOINTEL_STORAGE_PATH=/mnt/user/appdata/geointel/storage
|
|
GEOINTEL_POSTGIS_DATA_PATH=/mnt/user/appdata/geointel/postgres-data
|
|
```
|
|
|
|
`GEOINTEL_STORAGE_PATH` contains uploads, tiles, masks, reports and exports.
|
|
|
|
`GEOINTEL_POSTGIS_DATA_PATH` contains the embedded PostGIS database files.
|
|
|
|
## Local Ollama assistant
|
|
|
|
The repository Compose file, DockerMan template and automatic deployment all
|
|
map `host.docker.internal` to the Unraid host and enable the source-grounded
|
|
assistant by default. Ollama must already listen on host port `11434`;
|
|
GeoIntel does not install or expose Ollama itself.
|
|
|
|
```env
|
|
OLLAMA_ENABLED=true
|
|
OLLAMA_BASE_URL=http://host.docker.internal:11434
|
|
OLLAMA_DEFAULT_MODEL=qwen3.5:9b
|
|
OLLAMA_TIMEOUT_SECONDS=120
|
|
OLLAMA_MAX_OUTPUT_TOKENS=1200
|
|
OLLAMA_CONTEXT_TOKENS=16384
|
|
```
|
|
|
|
`/health/live` proves only that FastAPI is serving. `/health/ready` returns
|
|
HTTP 503 when PostgreSQL, PostGIS, the migration head or persistent storage is
|
|
not ready, and is the container healthcheck. The all-in-one startup also marks
|
|
work left in `running` by a previous process as failed with
|
|
`PROCESS_INTERRUPTED`; synchronous work cannot survive a container restart.
|
|
|
|
The model dropdown comes from Ollama `/api/tags`, so changing the installed
|
|
models requires no frontend rebuild. Verify after deployment with:
|
|
|
|
```bash
|
|
curl http://127.0.0.1:1202/api/v1/assistant/status
|
|
curl http://127.0.0.1:1202/api/v1/assistant/models
|
|
```
|
|
|
|
## Update from Gitea
|
|
|
|
```bash
|
|
cd /mnt/user/appdata/geointel
|
|
git fetch origin main
|
|
git reset --hard origin/main
|
|
bash deploy/unraid/deploy-release.sh
|
|
```
|
|
|
|
The equivalent low-level build remains available for debugging:
|
|
|
|
```bash
|
|
docker build --build-arg GEOINTEL_INSTALL_AI=${GEOINTEL_INSTALL_AI:-false} -f deploy/unraid/Dockerfile.all-in-one -t geointel-all-in-one:latest .
|
|
bash deploy/unraid/run-dockerman-container.sh
|
|
```
|
|
|
|
## Release identity, fresh install and rollback
|
|
|
|
The complete final-release order, including backup/restore, browser journeys,
|
|
SBOM, vulnerability policy, SSH-signed manifest and checksums, is in
|
|
`docs/RELEASE_RUNBOOK.md`. `VERSION` is the canonical semantic version and is
|
|
also written to the image's `org.opencontainers.image.version` label.
|
|
|
|
Inspect the running immutable revision and retained images:
|
|
|
|
```bash
|
|
docker inspect --format '{{index .Config.Labels "org.opencontainers.image.revision"}}' geointel
|
|
docker image ls geointel-all-in-one
|
|
```
|
|
|
|
Run a fresh install in isolated temporary PostGIS, storage and model paths. The
|
|
script binds only a random loopback port and removes its own container and
|
|
temporary directory:
|
|
|
|
```bash
|
|
bash scripts/verify_release_fresh_install.sh geointel-all-in-one:latest
|
|
```
|
|
|
|
Verify an upgrade against an isolated restore of a checksum-verified backup.
|
|
The generated temporary database is removed even when the check fails:
|
|
|
|
```bash
|
|
bash scripts/verify_release_upgrade_smoke.sh \
|
|
--backup-dir /mnt/user/appdata/geointel/backups/<verified-backup> \
|
|
--confirm-isolated-upgrade
|
|
```
|
|
|
|
Return to the image that was active immediately before the latest deployment:
|
|
|
|
```bash
|
|
bash deploy/unraid/rollback-dockerman-container.sh
|
|
```
|
|
|
|
For an older retained commit, select its immutable tag explicitly:
|
|
|
|
```bash
|
|
GEOINTEL_ROLLBACK_IMAGE=geointel-all-in-one:<commit-sha>-ai \
|
|
bash deploy/unraid/rollback-dockerman-container.sh
|
|
```
|
|
|
|
Rollback reuses the configured PostGIS and storage mounts and never runs an
|
|
Alembic downgrade. If a future release has a backward-incompatible migration,
|
|
restore its verified pre-release backup instead of forcing an older app
|
|
against a newer schema.
|
|
|
|
The configured upload limit is shared by FastAPI and the generated nginx
|
|
runtime configuration. Values outside `1..2048` MiB are rejected before the
|
|
active application is replaced.
|
|
|
|
## Data operations
|
|
|
|
`GEOINTEL_BACKUPS_PATH` defaults to
|
|
`/mnt/user/appdata/geointel/backups` and is mounted read-only at
|
|
`/app/backups`. Explicit cleanup commands can therefore verify a recent
|
|
backup without permission to alter it.
|
|
|
|
These commands are non-mutating:
|
|
|
|
```bash
|
|
docker exec geointel python /app/scripts/audit_data_operations.py
|
|
docker exec geointel python /app/scripts/cleanup_storage_artifacts.py
|
|
```
|
|
|
|
The full backup, confirmation, candidate-limit and apply sequence is in
|
|
`docs/DATA_OPERATIONS_RUNBOOK.md`. GeoIntel installs no automatic cleanup
|
|
schedule.
|
|
|
|
## Safe cleanup
|
|
|
|
Safe cache cleanup if Docker build cache fills the Unraid Docker image:
|
|
|
|
```bash
|
|
docker builder prune -af
|
|
```
|
|
|
|
Avoid broad volume pruning unless you explicitly intend to remove persisted PostGIS data or GeoIntel artifacts.
|
|
|
|
## Multi-container development stack
|
|
|
|
The root `docker-compose.yml` remains available for development and CI-like validation with separate `db`, `backend` and `frontend` services. For Unraid app-style operation, prefer `docker-compose.unraid.yml`.
|