Repository navigation
Description
Checked for duplicates
Yes - I've already checked
Describe the bug
With any WITH_*=true (tested with WITH_TITILER=true), the containerized MMGIS image behaves differently under Docker and Podman, and in both cases the in-container adjacent server fails silently.
scripts/server.js always calls adjacentServers(), which spawns start-<server>.sh inside the MMGIS container for each enabled server. adjacent-servers-proxy.js then sends the proxy route to localhost or to a compose service name (titiler, stac-fastapi, tipg, titiler-pgstac) depending on utils.isDocker(). isDocker() only checks for /.dockerenv or "docker" in /proc/self/cgroup.
- The in-container server exits immediately unless
adjacent-servers/<server>/.envexists.start-titiler.shrunspython -m dotenv run …, which fails withInvalid value for '-f' ".../adjacent-servers/titiler/.env" does not exist. The image doesn't contain that file, and a containerized setup (env file /environment:) has no reason to create it. The Adjacent Servers docs say the.envcopy steps are unneeded with Docker. - Nothing is logged when it exits. MMGIS logs
Starting titiler server on port 8883..., forwards only output lines containing "running", and has anerrorhandler but noexit/closehandler on the child. - Under Docker (
isDocker()true), the in-container server is never used. The proxy targetshttp://titiler:8883. If the in-container server does start (with a.env), it runs next to the compose service, using memory and a port while getting no traffic. Withdocker-compose.sample.yml+sample.env(flags default totrue, and the same.envis given to MMGIS), every enabled server is started twice, or would be, without point 1. - Under Podman,
isDocker()is false. Podman creates/run/.containerenv, not/.dockerenv, and its cgroup paths containlibpod, notdocker. The proxy targetslocalhost, so the in-container server is the one used and separate compose-style services are ignored. The TiTiler version served also changes: 0.24.2 in-image vs 1.2.1 in the compose file. IS_DOCKERis also sent to the browser and changes how relative COG paths are passed to TiTiler (/Missions/...vs../../Missions/...; still inconfigure/src/core/Maker.jsondevelopment). So the runtime also decides whether a separate TiTiler container can resolve local files.
Results with the 5.2.24 image (/titiler/healthz returns the TiTiler version, which shows which server answered):
| Runtime | titiler/.env |
Separate titiler service |
In-container server | /titiler/healthz via MMGIS |
|---|---|---|---|---|
| Docker | no | no | exits silently | 504 |
| Docker | no | yes | exits silently | 200 (1.2.1, service) |
| Docker | yes | no | running, unused | 504 |
| Docker | yes | yes | running, unused | 200 (1.2.1, service) |
| Podman | no | no | exits silently | 504 |
| Podman | yes | no | running | 200 (0.24.2, in-container) |
On a RHEL host running the image rootful under systemd with Podman 4.9.4 (cgroup v1), the container has only /run/.containerenv, and its /proc/self/cgroup entries are all /system.slice/<unit>.service/libpod-payload-<id>, so isDocker() is false in production too.
I checked the current development (9a04abc): adjacent-servers.js, start-*.sh, isDocker() and the proxy target selection are unchanged from 5.2.24.
What did you expect?
Whether MMGIS starts the adjacent servers itself, or proxies to separately run ones, should be an explicit setting, not a guess from the container runtime. A server MMGIS does start should either work without a hand-made .env or fail loudly.
Suggestions:
- An explicit mode, globally or per server, e.g.
ADJACENT_SERVERS_MODE=embedded|external, orTITILER_URL=http://titiler:8883meaning external. Spawn only in embedded mode; proxy tolocalhostin embedded mode and to the configured URL otherwise. This would also replace theIS_DOCKERpath switch in point 5. - At minimum: don't call
adjacentServers()when the proxy won't targetlocalhost, and haveisDocker()also recognize Podman (/run/.containerenv), or rename it to what it actually decides. - Make
start-*.shnot require a.env: only pass it todotenvif it exists, or just runpython -m uvicorn, since the child already inherits MMGIS's environment. - Log the child's exit code and its last stderr lines on
exit.
Reproducible steps
# env for MMGIS: AUTH=off, WITH_TITILER=true, all other WITH_*=false, DB_* pointing at a postgis container on the same network
docker network create adj && docker run -d --name db --network adj -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=mmgis postgis/postgis:17-3.5-alpine
# Docker: isDocker() true, proxy -> titiler:8883 (doesn't exist), in-container server exited
docker run -d --name mmgis --network adj --env-file test.env ghcr.io/nasa-ammos/mmgis:5.2.24-release
docker exec mmgis curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8888/titiler/healthz # 504
docker exec mmgis sh -c 'cd adjacent-servers/titiler && sh start-titiler.sh 8883' # dotenv: .env does not exist
# Podman: same image and env; isDocker() false, proxy -> localhost:8883
podman exec mmgis ls /.dockerenv /run/.containerenv # only /run/.containerenv
# with an empty adjacent-servers/titiler/.env created in the container: /titiler/healthz -> 200, version 0.24.2Environment
- Version: 5.2.24 (ghcr.io/nasa-ammos/mmgis:5.2.24-release); same code on development @ 9a04abc5
- Docker 29.2.1 (Colima, arm64)
- Podman 4.9.4 (rootful; tested locally, and confirmed on a RHEL host under systemd, cgroup v1)
- Separate TiTiler: ghcr.io/developmentseed/titiler:1.2.1Related: #753. The internal-hostname leak through the proxy is a separate bug, but it is affected by the same isDocker() target choice.
Activity
Fix NASA-AMMOS#1138: validate COG paths and harden Configure startup …