How the GeoLibre Docker Container Bundles Nginx and the Python Sidecar

The GeoLibre Docker image uses a multi-stage build that combines a static nginx-served frontend with a Python FastAPI sidecar, reverse-proxied behind the same nginx instance at the /sidecar path.

This article explains how the opengeos/GeoLibre repository packages a web-based geospatial application and its optional conversion service into a single production-ready container. The architecture separates build-time asset generation from runtime delivery, enabling efficient caching and minimal final image size.

Multi-Stage Build Architecture

The Dockerfile implements a two-stage build process that isolates Node.js build dependencies from the Python runtime environment.

Stage 1: Node Build Environment

The first stage uses node:22-alpine with platform awareness to compile the frontend assets.

FROM --platform=$BUILDPLATFORM node:22-alpine AS builder

Key build steps in this stage:

  1. Layer caching optimization — All workspace package.json files are copied before the main source tree, allowing npm ci to create a cached dependency layer that only invalidates when packages change.
  2. Full source copy — After dependency installation, the complete source tree is copied into the build context.
  3. Production build — Running npm run build generates optimized static assets in apps/geolibre-desktop/dist.

Stage 2: Python Runtime Environment

The runtime stage uses python:3.12-slim-bookworm specifically for glibc compatibility with pre-built Python wheels.

Core runtime dependencies:

  • nginx — Static file server and reverse proxy
  • libexpat1 — XML parsing support
  • openssl — Cryptographic operations

Sidecar installation:

The FastAPI sidecar source from backend/geolibre_server is copied to /opt/geolibre_server and installed alongside duckdb and rio-cogeo wheels. Platform-specific handling includes freestiler and whitebox-workflows wheels for amd64 only—they are skipped on arm64 due to unavailable pre-built binaries.

Nginx Configuration and Routing

The nginx setup bridges the frontend and sidecar services under a unified host.

Configuration Template

The file docker/nginx.conf serves as a template copied to /etc/nginx/nginx.conf.template during image build. This template supports runtime variable substitution for:

  • Sidecar authentication token — Injected per container launch for security isolation
  • Optional Basic Auth — Conditionally enabled via environment variables

Runtime Configuration Generation

The docker/entrypoint.sh script renders the template into /etc/nginx/conf.d/default.conf on container startup. This dynamic generation allows credential injection without rebuilding the image.

Static asset serving:

Built web assets from apps/geolibre-desktop/dist are placed in /usr/share/nginx/html, making the UI available at the container root (/).

Sidecar proxying:

All FastAPI endpoints are reverse-proxied through /sidecar, enabling same-origin access to conversion APIs without CORS configuration.

Environment Configuration

The container accepts several environment variables to configure sidecar behavior:

Variable Purpose
GEOLIBRE_CONVERSION_ROOTS Colon-separated paths the sidecar may access for file operations
GEOLIBRE_CONVERSION_PYTHON Python interpreter path for conversion scripts
GEOLIBRE_AUTH_USER Basic Auth username (optional)
GEOLIBRE_AUTH_PASSWORD Basic Auth password hash (optional)

Health Check and Startup

The image includes a HEALTHCHECK directive that pings /healthz to verify sidecar responsiveness. This endpoint is explicitly exempt from Basic Auth requirements to ensure reliable orchestration health probes.

Container startup executes entrypoint.sh, which:

  1. Renders the nginx configuration template
  2. Injects authentication credentials and sidecar token
  3. Launches nginx as the primary process
  4. Starts the sidecar via uvicorn as a background service

Building and Running the Container

Build the Image

docker build -t geolibre:latest .

Run with Default Settings

Mount a local data directory to /data for file access:

docker run -d -p 8080:80 \
  -v "$(pwd)/data:/data" \
  --name geolibre \
  geolibre:latest

Enable Basic Authentication

Provide credentials via environment variables at runtime:

docker run -d -p 8080:80 \
  -e GEOLIBRE_AUTH_USER=admin \
  -e GEOLIBRE_AUTH_PASSWORD=$(openssl passwd -apr1 secret) \
  -v "$(pwd)/data:/data" \
  --name geolibre \
  geolibre:latest

Access the Sidecar API

Use the reverse-proxied path to reach conversion endpoints:

curl http://localhost:8080/sidecar/convert/vector

Key Source Files

Understanding these files provides complete visibility into the bundling approach:

  • Dockerfile — Multi-stage build orchestrating Node and Python environments
  • docker/nginx.conf — Server template with proxy rules and Content-Security-Policy headers
  • docker/entrypoint.sh — Runtime configuration generator and process supervisor
  • backend/geolibre_server/ — FastAPI implementation providing vector, raster, and Whitebox geoprocessing APIs
  • apps/geolibre-desktop/dist/ — Compiled static assets served by nginx

Summary

  • The two-stage build separates Node.js compilation from Python runtime, optimizing layer caching and final image size.
  • Nginx serves dual roles as static file server for the frontend and reverse proxy for the sidecar at /sidecar.
  • Runtime configuration via entrypoint.sh enables dynamic credential injection without image rebuilds.
  • Platform-aware builds conditionally include freestiler and whitebox-workflows only on amd64 where wheels exist.
  • Health checks verify sidecar availability while remaining accessible to container orchestrators.

Frequently Asked Questions

How does the sidecar communicate with the main application?

The sidecar runs as a separate process within the same container, accessed via nginx's reverse proxy at the /sidecar path. This same-origin routing eliminates CORS requirements and provides unified ingress through port 80.

Can I run the nginx frontend without the Python sidecar?

The entrypoint.sh script starts both services by default. To disable the sidecar, you would need to override the container command or modify the entrypoint, though this is not the intended deployment pattern in the opengeos/GeoLibre source.

Why use python:3.12-slim-bookworm instead of alpine for the runtime stage?

The slim-bookworm variant provides glibc compatibility required for pre-built wheels including duckdb and rio-cogeo. Alpine's musl libc often fails with many scientific Python packages that distribute only manylinux wheels.

How is the sidecar token secured?

A unique token is generated on each container launch by entrypoint.sh and injected into the nginx configuration. This token must be included in sidecar requests, providing request authentication without persistent credential storage in the image.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →