# Self-Hosting GeoLibre with CDN-Free Builds: A Complete Guide to Offline Deployment

> Learn how to self-host GeoLibre with CDN-free builds. This guide explains setting environment flags to bundle WASM engines locally for offline deployment. Get started today.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: how-to-guide
- Published: 2026-08-15

---

**Set environment flags like `GEOLIBRE_NO_EXTERNAL_CDN=1` during the Vite build process to bundle all WASM engines locally and eliminate external CDN dependencies.**

GeoLibre's default web distribution fetches heavy WebAssembly assets—**Pyodide**, **PGlite/PostGIS**, **CereusDB**, **gdal3.js**, and **DuckDB-WASM**—from public CDNs such as **jsDelivr** and **unpkg.com**. For enterprises, air-gapped networks, or privacy-focused deployments, the codebase provides granular environment flags that control whether each asset is fetched at runtime or bundled into your application. This article explains how to self-host GeoLibre using these CDN-free build options.

## Core CDN-Removal Flags

GeoLibre implements a hierarchical flag system in [`apps/geolibre-desktop/vite.config.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/vite.config.ts) and `docker/Dockerfile`. The master flag overrides all others, with individual flags controlling specific engines.

| Flag | Default | Effect when cleared | Use case |
|:---|:---|:---|:---|
| `GEOLIBRE_NO_EXTERNAL_CDN` | unset | Strips **all** GeoLibre-controlled CDN references. Disables story-map HTML export, ONNX runtime, 3D Tiles Draco/KTX2 decoders, and GDAL export. Forces other CDN flags to `0`. | Enterprise deployments with strict no-CDN policies |
| `GEOLIBRE_PGLITE_CDN` | `1` (CDN) | Bundles PGlite + PostGIS WASM (~22 MiB) into `/assets/` | Offline SQL + PostGIS support |
| `GEOLIBRE_CEREUS_CDN` | `1` (CDN) | Bundles CereusDB (Apache Sedona) WASM (~40 MiB) | Offline spatial analytics |
| `GEOLIBRE_GDAL_CDN` | `1` (CDN) | Disables [`gdal3.js`](https://github.com/opengeos/GeoLibre/blob/main/gdal3.js) export feature entirely | When client-side GeoTIFF/COG export is unnecessary |
| `GEOLIBRE_DUCKDB_WASM_CDN` | `0` (bundled) | Moves DuckDB-WASM (~40 MiB) to jsDelivr | Cloudflare Pages/Workers deployments (25 MiB asset limit) |
| `VITE_PYODIDE_INDEX_URL` | jsDelivr URL | Redirects Pyodide to a custom mirror | Private Pyodide hosting without disabling Python support |

These flags are documented in [`docs/self-hosting.md`](https://github.com/opengeos/GeoLibre/blob/main/docs/self-hosting.md#L151) and [`docs/getting-started.md`](https://github.com/opengeos/GeoLibre/blob/main/docs/getting-started.md#L590-L594).

## Building a Fully Offline-Capable Distribution

### Complete CDN-Free Build

Eliminate all external GeoLibre CDN references:

```bash
GEOLIBRE_NO_EXTERNAL_CDN=1 npx vite build

```

This configuration:

- Emits all bundleable assets to `/assets/`
- Removes external script tag injections
- Disables features requiring exclusive CDN URLs (story-map export, object detection, GDAL export)

**Trade-offs**: Certain advanced capabilities become unavailable. Core GIS functions—vector rendering, raster analysis, 3D Tiles viewing, and the processing toolbox—remain fully operational.

### Selective Engine Bundling

Bundle specific WASM engines while retaining other CDN dependencies:

```bash
GEOLIBRE_PGLITE_CDN=0 GEOLIBRE_CEREUS_CDN=0 npx vite build

```

Result: PGlite/PostGIS and CereusDB assets are copied into `/assets/`, enabling offline SQL processing and Sedona-style analytics without bundling the full CDN-free stack.

## Self-Hosting Pyodide on Private Infrastructure

Pyodide requires special handling. The `VITE_PYODIDE_INDEX_URL` flag redirects fetches without disabling Python support:

```bash
VITE_PYODIDE_INDEX_URL=https://my.cdn.example.com/pyodide/ \
GEOLIBRE_NO_EXTERNAL_CDN=1 npx vite build

```

GeoLibre itself becomes CDN-free; Python + GeoPandas functionality loads from your controlled infrastructure. Mirror the complete Pyodide distribution—including `pyodide.asm.wasm`, `python_stdlib.zip`, and package files—to this endpoint.

## Docker-Based Self-Hosted Deployment

The repository provides production-ready containerization in `docker/Dockerfile` and [`docker/nginx.conf`](https://github.com/opengeos/GeoLibre/blob/main/docker/nginx.conf):

```bash

# Build the CDN-free bundle

GEOLIBRE_NO_EXTERNAL_CDN=1 npx vite build

# Construct the container image

docker build -t my-geolibre .

# Run with exposed port

docker run -p 8080:80 my-geolibre

```

The Dockerfile copies `./dist` to `/usr/share/nginx/html`, serving all bundled assets from the same origin. Customize [`docker/nginx.conf`](https://github.com/opengeos/GeoLibre/blob/main/docker/nginx.conf) to add your own CDN mirrors or implement additional security headers.

## Performance and Architecture Implications

### Service Worker Behavior

With bundled assets, the service worker's **CacheFirst** strategy for CDN engines becomes unnecessary. Assets served from `/assets/` leverage standard HTTP caching and same-origin security policies. The [`vite.config.ts`](https://github.com/opengeos/GeoLibre/blob/main/vite.config.ts) logic adjusts cache rules automatically based on build flags.

### Binary Size Budget

| Configuration | Size Impact |
|:---|:---|
| `GEOLIBRE_PGLITE_CDN=0` | +~22 MiB |
| `GEOLIBRE_CEREUS_CDN=0` | +~40 MiB |
| `GEOLIBRE_GDAL_CDN=0` | -~40 MiB (complete removal) |
| `GEOLIBRE_DUCKDB_WASM_CDN=1` | -~40 MiB (moves to external CDN) |

Calculation: A fully bundled desktop installer or web deployment with PGlite and CereusDB but no GDAL export adds approximately **62 MiB** to the base build.

### Cloudflare Pages Compatibility

Static hosting platforms often enforce per-asset size limits. GeoLibre's DuckDB-WASM flag specifically addresses Cloudflare's **25 MiB** threshold:

```bash
GEOLIBRE_DUCKDB_WASM_CDN=1 npx vite build

```

This moves the ~40 MiB DuckDB-WASM distribution to jsDelivr while keeping GeoLibre's own code self-hosted.

## Embed Build Customization

The script `scripts/build-embed.mjs` generates `@geolibre/embed`, a distributable iframe-compatible version. This build respects the same environment flags:

```bash
GEOLIBRE_NO_EXTERNAL_CDN=1 GEOLIBRE_PGLITE_CDN=0 npx vite build
node scripts/build-embed.mjs

```

Result: A CDN-free embeddable map component suitable for intranet portals or restricted environments.

## Critical Source Files Reference

| File | Purpose |
|:---|:---|
| [`apps/geolibre-desktop/vite.config.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/vite.config.ts) | Reads environment variables, configures Vite build pipeline, service-worker cache rules |
| `docker/Dockerfile` | Production container assembly, copies bundled assets to Nginx |
| [`docker/nginx.conf`](https://github.com/opengeos/GeoLibre/blob/main/docker/nginx.conf) | Static asset serving configuration |
| [`docs/self-hosting.md`](https://github.com/opengeos/GeoLibre/blob/main/docs/self-hosting.md) | Master CDN-free deployment documentation (#L151) |
| [`docs/getting-started.md`](https://github.com/opengeos/GeoLibre/blob/main/docs/getting-started.md) | Per-engine flag reference table (#L590-594) |
| `scripts/build-embed.mjs` | CDN-free embeddable build generator |
| [`packages/processing/src/ort.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/processing/src/ort.ts) | ONNX Runtime WASM URL derivation—CDN flag removes this dependency |

## Summary

- **Master control**: Set `GEOLIBRE_NO_EXTERNAL_CDN=1` to eliminate all GeoLibre-managed CDN references
- **Granular bundling**: Use individual `GEOLIBRE_*_CDN` flags to select which WASM engines ship with your build
- **Pyodide flexibility**: Redirect to private mirrors via `VITE_PYODIDE_INDEX_URL` without losing Python support
- **Platform constraints**: Enable `GEOLIBRE_DUCKDB_WASM_CDN` for size-limited static hosts
- **Production deployment**: Leverage the provided Docker configuration for same-origin asset serving

## Frequently Asked Questions

### What features break when I disable all CDNs?

Story-map HTML export, built-in ONNX-based object detection, 3D Tiles Draco/KTX2 decoders, and GDAL export functionality become unavailable. All vector, raster, 3D visualization, and processing toolbox operations continue working because their WASM engines either bundle locally or remain fetchable from infrastructure you control.

### Can I self-host Pyodide while keeping other engines on CDNs?

Yes. Set `VITE_PYODIDE_INDEX_URL` to your private mirror without enabling `GEOLIBRE_NO_EXTERNAL_CDN`. PGlite, CereusDB, and other engines continue loading from public CDNs while Pyodide fetches exclusively from your specified origin.

### How do I verify my build contains no external CDN references?

Inspect the generated [`index.html`](https://github.com/opengeos/GeoLibre/blob/main/index.html) and JavaScript chunks in `/dist`. Search for patterns matching `unpkg.com`, `cdn.jsdelivr.net`, or `jsDelivr`. The [`vite.config.ts`](https://github.com/opengeos/GeoLibre/blob/main/vite.config.ts) logic strips these URLs when `GEOLIBRE_NO_EXTERNAL_CDN` is active, and service-worker precache manifests will list only same-origin `/assets/` paths.

### What's the minimum viable CDN-free configuration for offline desktop use?

For Tauri or Electron deployments targeting offline-first scenarios: build with `GEOLIBRE_NO_EXTERNAL_CDN=1 GEOLIBRE_PGLITE_CDN=0`. This provides core GIS capabilities plus SQL/PostGIS processing without network dependencies, at a cost of approximately +22 MiB in installer size.