Most GeoLens installs are prebuilt-image installs — the ones created with
curl -fsSL https://getgeolens.com/install.sh | sh. Their .env records
COMPOSE_FILE=docker-compose.prod.yml and a pinned GEOLENS_VERSION. For these,
the recommended upgrade is the one-command, backup-first flow:
Terminal window
# From your install directory
./scripts/upgrade.sh# upgrade to the newest published release
./scripts/upgrade.sh1.3.0# or pin an explicit target version
scripts/upgrade.sh (equivalently, bash scripts/install.sh --upgrade) performs, in order:
Pre-upgrade backup — quiesces api + worker, then pg_dump -Fc to a timestamped file under backups/pre-upgrade/. The upgrade aborts if the dump is missing or empty (nothing else is touched).
Pins the new GEOLENS_VERSION in .env.
Pulls the prebuilt images (docker compose pull --ignore-buildable).
Runs migrations via the fail-closed one-shot migrate service — a failed migration aborts before the app is started.
Starts the stack and waits for every service to report healthy.
On success it keeps the pre-upgrade dump and prints the rollback
recipe for reference; on any failure it stops, leaves your data in the dump,
and prints the same recipe.
If you instead build from source (git clone install, COMPOSE_FILE=docker-compose.yml),
upgrade by updating the checkout and rebuilding — see
Source-build upgrade below. scripts/upgrade.sh
detects a source install and prints those instructions instead of running.
Back up your database before any major upgrade. Use a custom-format (-Fc) dump — the format scripts/restore.sh expects for rollback. (scripts/upgrade.sh takes this backup for you automatically; this is the manual equivalent.)
Rollback is re-pin the previous version + restore the pre-upgrade -Fc dump.
Schema migrations move forward only — alembic downgrade is not a supported
rollback, and you must neverpsql < dump a custom-format (-Fc) file (it is
not plain SQL). Restore with scripts/restore.sh, which validates the dump and
restores it via pg_restore.
Terminal window
# 1. Re-pin the previous version in .env (edit the GEOLENS_VERSION= line).
# Image tags are bare semver, e.g. the version you upgraded FROM:
# GEOLENS_VERSION=1.2.4
# 2. Restore the pre-upgrade dump the upgrade created. restore.sh stops
# api/worker, runs pg_restore, and restarts them afterward.
A failed scripts/upgrade.sh run prints this exact recipe (with the real dump
path filled in) before it exits, so you can copy it from the upgrade output.
For installs that build from source (COMPOSE_FILE=docker-compose.yml),
upgrade by updating the checkout and rebuilding instead of pulling images. Take a
-Fc backup first (step 1 above), then:
Terminal window
# 1. Update the checkout to the new release tag.
gitfetch--tagsorigin
gitcheckoutv1.3.0# replace with your target tag
# 2. Rebuild the images from the new source.
dockercompose-fdocker-compose.ymlbuild
# 3. Run migrations (fail-closed) BEFORE starting the app.
dockercompose-fdocker-compose.ymlup-dmigrate
dockercompose-fdocker-compose.ymllogsmigrate# confirm it exited 0
# 4. Bring the stack up and verify health.
dockercompose-fdocker-compose.ymlup-d
dockercompose-fdocker-compose.ymlps
To roll a source build back, restore the -Fc dump with scripts/restore.sh
(as above) and git checkout the previous tag — never alembic downgrade.
The backend now enforces a 32-character minimum on JWT_SECRET_KEY at startup (HS256 requires ≥ 256 bits of entropy). A deployment with a shorter secret will fail fast on the next restart with:
FATAL: JWT_SECRET_KEY must be at least 32 characters. Generate one with: openssl rand -hex 32
Before upgrading, verify your secret length:
Terminal window
echo-n"$JWT_SECRET_KEY"|wc-c
If it reports fewer than 32, generate a replacement and update your .env:
Terminal window
JWT_SECRET_KEY=$(opensslrand-hex32)
Rotating the key invalidates all issued JWT tokens — all users will be logged out and need to sign in again. Plan the rotation during a low-traffic window, or coordinate with your user base.
.env.example ships JWT_SECRET_KEY= empty, and the validator also rejects well-known public placeholders such as dev-only-change-me-in-production. scripts/install.sh generates a real openssl rand -hex 32 secret on first run, so installer-driven deployments are unaffected; hand-edited .env files must set a real openssl rand -hex 32 value.
Other env hardening in this release:
Secret fields (POSTGRES_PASSWORD, JWT_SECRET_KEY, GEOLENS_ADMIN_PASSWORD, ANTHROPIC_API_KEY, OPENAI_API_KEY, S3_SECRET_ACCESS_KEY, TILE_SIGNING_SECRET) are now stored as Pydantic SecretStr internally. Values are masked in logs, repr(), and validation-error output. Application behavior is unchanged — this is a defense-in-depth improvement.
LOG_LEVEL values are now validated against the stdlib logging set (DEBUG, INFO, WARNING, ERROR, CRITICAL). A typo like LOG_LEVEL=verbose now fails at startup instead of crashing later.
Two previously undocumented env vars — ENV_ONLY_CONFIG and GEOLENS_EDITION — are now documented in .env.example. Neither is required.
The backend/.env symlink has been removed. Host-side workflows (cd backend && uv run pytest) now resolve ./.env at the project root via the Settings env_file path. No action required unless you had local scripts depending on backend/.env as a literal path.
VITE_API_PROXY_TARGET (docker-compose.yml frontend service) was renamed to API_PROXY_TARGET. The old name still works for one release via a fallback in vite.config.ts — update your local compose overrides when convenient.
The landing page has been removed. The root route (/) now serves the Search page directly. The SHOW_LANDING_PAGE environment variable has been removed from backend config and the branding API.
What this means:
Existing bookmarks to / will show the Search page instead of the landing page.
The /search route redirects to / — existing /search bookmarks continue to work.
Remove SHOW_LANDING_PAGE from your .env if present (it is ignored but produces no error).
GeoLens 1.0.0 is the first public release. Prior to 1.0.0, the project was internally versioned as 2.0 → 13.0 during pre-public development. Those legacy versions never shipped to anyone outside the project.
If you somehow have a checkout from a pre-1.0.0 internal build:
No data migration is required. The 1.0.0 schema is compatible with the most recent pre-public versions; Alembic migrations apply normally on the first 1.0.0 startup.
The version number resets, but the codebase moves forward. 1.0.0 is the cumulative state of all prior internal work, not a downgrade.
No git checkout v13.x rollback path exists from 1.0.0. If you need to roll back, restore from the database backup you took before upgrading (see Pre-upgrade checklist).
No environment variables changed at the 1.0.0 boundary. Your existing .env from any pre-public build continues to work without modification.
For all subsequent upgrades, follow the standard procedure above.