Skip to main content

Install with Docker

Andromeda runs as two containers, the broker and a PostgreSQL server with PostGIS and TimescaleDB. Any PostgreSQL image with those two extensions will do, as long as it has at least PostgreSQL 15, PostGIS 3.0.0 and TimescaleDB 2.31.0 and loads TimescaleDB at startup. PostgreSQL lists everything the broker checks.

TimescaleDB comes in two editions, and Andromeda supports both. With the TSL (Community) edition, the broker moves older history into TimescaleDB's compressed columnstore, which saves disk space. With the Apache-2.0 edition, history stays uncompressed and everything else works the same, so you can run the whole stack on open-source software.

To make the setup easier, Andromeda publishes a PostgreSQL image of its own, in both editions, with the extensions already installed and its settings tuned for the broker. You don't have to use it, but the setup below does, because it is the shortest way to a working broker. The broker image and the PostgreSQL image are both built for linux/amd64 and linux/arm64, so the same commands work on an Intel or Arm server and on a Mac with Docker Desktop.

Run it​

In an empty directory, generate a database password into .env, which Compose reads on its own.

echo "POSTGRES_PASSWORD=$(openssl rand -hex 24)" > .env

That is 48 random hexadecimal characters. Any password works, but it guards every tenant's data, so use a long random one. Hexadecimal also needs no escaping in the database URL below. Then add docker-compose.yaml:

docker-compose.yaml
# Andromeda and its PostgreSQL server. Generate the database password into .env first, which Compose reads by itself:
#
# echo "POSTGRES_PASSWORD=$(openssl rand -hex 24)" > .env
#
# Hexadecimal needs no escaping in the database URL below.

name: andromeda

services:
postgresql:
image: ghcr.io/mskvarc/postgresql:18.6-timescaledb2.31.0-postgis3.6.4-tsl
restart: unless-stopped
environment:
POSTGRES_USER: andromeda
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
POSTGRES_DB: andromeda
# The image tunes PostgreSQL to these limits; without them it tunes for the whole host.
mem_limit: 4g
cpus: 2
# Dynamic shared memory grows with the tenant databases, and Docker's default /dev/shm is 64 MB.
shm_size: 1gb
# PostgreSQL 18 images keep their data under /var/lib/postgresql, not /var/lib/postgresql/data.
volumes:
- postgresql-data:/var/lib/postgresql

andromeda:
image: ghcr.io/mskvarc/andromeda:1.0.0
restart: unless-stopped
# The broker stops within server.graceful_drain_timeout plus notifications.evaluation.drain_timeout,
# 60 seconds by default, and Docker waits only 10 seconds before it kills a container.
stop_grace_period: 70s
# A broker that can't reach the database within a few seconds of starting stops with an error.
depends_on:
postgresql:
condition: service_healthy
environment:
ANDROMEDA__DATABASE__CONNECTION: postgres://andromeda:${POSTGRES_PASSWORD}@postgresql:5432/andromeda
ports:
- "1056:1056"

volumes:
postgresql-data:

Start it:

docker compose up -d

Compose pulls both images, starts postgresql, waits until it is healthy, then starts andromeda.

To stop, upgrade or remove it:

docker compose stop # keep the data
docker compose pull && docker compose up -d # upgrade to what the tags point at
docker compose down # remove the containers, keep the data
docker compose down -v # remove the containers and the data

The broker connects to the database and migrates it at startup, so there is no separate setup step. Check that it is serving:

curl -i http://localhost:1056/ngsi-ld/v1/health
curl http://localhost:1056/ngsi-ld/v1/version

The first answers 200 with the health report. The second lists the versions of the broker, PostgreSQL, PostGIS and TimescaleDB, the TimescaleDB edition, and whether the database allows enough connections for the broker's pools. Then go to the Quickstart.

What the setup does
  • On its first start, the image creates the role andromeda as a superuser. The broker needs a role that can create databases and extensions, because it makes one database per tenant and installs PostGIS and TimescaleDB in each.
  • The data lives in the volume postgresql-data, mounted at /var/lib/postgresql, the path PostgreSQL 18 images use. Mount it there and nowhere else.
  • The database container gets 1 GB of shared memory. PostgreSQL's dynamic shared memory grows with the number of tenant databases, and Docker gives a container only 64 MB unless told otherwise.
  • The broker reaches the database by its name, postgresql. No database port is published, so nothing outside the Docker network can connect to it.
  • The broker listens on 0.0.0.0:1056 by default, so the published port reaches it without a host setting.
  • A broker that can't reach the database within database.acquire_timeout, 5 seconds by default, stops with an error. That is why it waits for the database to be healthy.
  • The broker image has a health check of its own, which runs andromeda health inside the container, so docker ps shows when the broker is ready. Broker image explains what it probes and what else is in the image.
  • The broker stops within server.graceful_drain_timeout plus notifications.evaluation.drain_timeout, 60 seconds by default. Docker kills a container 10 seconds after it asks it to stop, so the setup gives the broker 70 seconds, with stop_grace_period in Compose and --stop-timeout with docker run.
  • With their defaults, the broker's pools hold at most 152 connections, which fits the 200 the image allows, and keep up to 16 tenants loaded. That is enough for up to a few dozen tenants. For more, see Choosing a setup.

Images and tags​

The two images are versioned separately, and each has tags that move. Pick a tag by how you want to upgrade. A broker tag with the full version never changes, so nothing upgrades until you edit it. A minor or major tag takes new releases each time you pull, so upgrading is docker compose pull and a restart. The PostgreSQL image is rebuilt every week, and only its dated tags stay put.

Broker tags​

TagMovesExample
Full versionNever1.0.0
MinorWith each patch release1.0
MajorWith each minor and patch release, until the next major version1
latestWith each releaselatest

A pre-release such as 1.1.0-rc.1 gets no moving tag, so you pull it by its full version.

PostgreSQL image​

The postgresql image has its own version, which follows PostgreSQL, TimescaleDB and PostGIS, and the last part of each tag names the edition, tsl or apache. The setup above uses the TSL edition. Its tag names the versions inside, but the image behind it is rebuilt every Monday with security fixes. To keep the same image across restarts, use the tag with a build date appended, such as 18.6-timescaledb2.31.0-postgis3.6.4-tsl-20261105.42, or the image digest. For the Apache-2.0 edition, use the tag 18.6-timescaledb2.31.0-postgis3.6.4-apache instead. PostgreSQL image explains the tags and editions, what the image tunes, and how to verify and upgrade it.

To use another image, change the image of the postgresql service and check the image against PostgreSQL. Three parts of the setup above rely on the Andromeda image, so check them too.

  • The broker waits for service_healthy. An image without a health check of its own needs one added to the postgresql service.
  • The broker's pools can need 152 connections, and PostgreSQL allows 100 unless told otherwise. Raise max_connections, or the broker logs database_connection_capacity_exceeded and requests can fail under load.
  • An image of PostgreSQL 17 or older keeps its data in /var/lib/postgresql/data, so mount the volume there.

Configuring the broker​

The broker image carries no configuration file, so the broker runs on its defaults and on the environment, where the setup above sets database.connection, the one setting without a default. Configuration explains the settings and how environment variables and a file combine. What Docker changes is how you write, check and mount the file.

Write a file with every key and its default:

docker run --rm ghcr.io/mskvarc/andromeda:1.0.0 configuration generate > andromeda.toml

database.connection has no default, so the file shows it commented out. Leave it there and keep passing ANDROMEDA__DATABASE__CONNECTION, so the password stays in .env. Check the file before you start the broker:

docker run --rm -v "$PWD/andromeda.toml:/etc/andromeda/andromeda.toml:ro" \
ghcr.io/mskvarc/andromeda:1.0.0 \
--configuration /etc/andromeda/andromeda.toml configuration validate

Then mount it into the broker's container and point the broker at it.

Add the mount and the flag to the andromeda service, and give the health check the same flag so it probes the listener the file configures.

andromeda:
command: ["--configuration", "/etc/andromeda/andromeda.toml"]
healthcheck:
test: ["CMD", "andromeda", "--configuration", "/etc/andromeda/andromeda.toml", "health"]
volumes:
- ./andromeda.toml:/etc/andromeda/andromeda.toml:ro

Before you rely on it​

  • Choose how the tags move. andromeda:1.0.0 changes only when you edit it. andromeda:1.0 takes patch releases on each pull, and andromeda:1 takes minor releases too, which keeps upgrades easy. Every PostgreSQL tag without a build date, the full one in the setup above included, moves to a rebuilt image each week. Pin the dated tag or the digest, as PostgreSQL image explains.

  • Back up the volume. postgresql-data holds everything. Use a physical backup tool with WAL archiving against the running server, as PostgreSQL describes. A copy of the volume taken while the server runs can hold files that don't agree with each other.

  • Keep the database private. Publish no port for postgresql. If a tool outside Docker must reach it, publish it on 127.0.0.1 only, as "127.0.0.1:5432:5432".

  • Consider a reverse proxy. The broker serves plain HTTP on 1056 by default, and it terminates TLS itself once you give it a certificate. A reverse proxy or API gateway in front of it is still worth having, because it can apply rules of your own before a request reaches the broker. TLS covers the broker's own TLS, and Network security covers what to put in front of it.

  • Verify the images. Both are signed with Sigstore cosign. To check the broker image:

    cosign verify ghcr.io/mskvarc/andromeda:1.0.0 \
    --certificate-identity-regexp 'https://github.com/mskvarc/andromeda/' \
    --certificate-oidc-issuer https://token.actions.githubusercontent.com

If it goes wrong​

  • The broker stops right after it starts, with a database error

    It started before the database accepted connections, or the host or password in ANDROMEDA__DATABASE__CONNECTION is wrong. Look at docker compose logs postgresql and docker compose logs andromeda, and make sure the password in the URL matches the one in .env.

  • The log shows database_connection_capacity_exceeded at warn

    The broker's pools could hold more connections than max_connections allows. See Connection capacity.

  • The database won't start, a new password has no effect, or a tenant answers 500

    These come from the PostgreSQL image, and PostgreSQL image covers each.

  • A container exits with exec format error

    The image was pulled for another architecture. Pull it again on the machine that runs it, or pass --platform linux/amd64 or linux/arm64.

Troubleshooting covers the rest of a first run.