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
- Docker Compose
- Docker
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:
# 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
Generate a password, create a network and start the database. Any password works, but it guards every tenant's data, so use a long random one.
export POSTGRES_PASSWORD=$(openssl rand -hex 24)
docker network create andromeda
docker run -d --name postgresql --network andromeda \
--restart unless-stopped \
--memory 4g --cpus 2 --shm-size 1g \
-e POSTGRES_USER=andromeda \
-e POSTGRES_PASSWORD \
-e POSTGRES_DB=andromeda \
-v postgresql-data:/var/lib/postgresql \
ghcr.io/mskvarc/postgresql:18.6-timescaledb2.31.0-postgis3.6.4-tsl
Wait until it accepts connections, then start the broker:
until docker exec postgresql pg_isready -q -h 127.0.0.1; do sleep 1; done
docker run -d --name andromeda --network andromeda \
--restart unless-stopped \
--stop-timeout 70 \
-p 1056:1056 \
-e ANDROMEDA__DATABASE__CONNECTION="postgres://andromeda:$POSTGRES_PASSWORD@postgresql:5432/andromeda" \
ghcr.io/mskvarc/andromeda:1.0.0
Keep the password somewhere safe. The database reads it only on its first start, so exporting a new one later changes nothing. A password with characters such as @, / or : must be percent-encoded inside ANDROMEDA__DATABASE__CONNECTION, which hexadecimal never needs.
To remove everything, including the data:
docker rm -f andromeda postgresql
docker volume rm postgresql-data
docker network rm andromeda
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.
- On its first start, the image creates the role
andromedaas 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:1056by 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 healthinside the container, sodocker psshows 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_timeoutplusnotifications.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, withstop_grace_periodin Compose and--stop-timeoutwithdocker 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
| Tag | Moves | Example |
|---|---|---|
| Full version | Never | 1.0.0 |
| Minor | With each patch release | 1.0 |
| Major | With each minor and patch release, until the next major version | 1 |
latest | With each release | latest |
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 thepostgresqlservice. - The broker's pools can need 152 connections, and PostgreSQL allows 100 unless told otherwise. Raise
max_connections, or the broker logsdatabase_connection_capacity_exceededand 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.
- Docker Compose
- Docker
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
Add the mount to the broker's docker run, and put the flag after the image name:
-v "$PWD/andromeda.toml:/etc/andromeda/andromeda.toml:ro"
--configuration /etc/andromeda/andromeda.toml
Before you rely on it
-
Choose how the tags move.
andromeda:1.0.0changes only when you edit it.andromeda:1.0takes patch releases on each pull, andandromeda:1takes 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-dataholds 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 on127.0.0.1only, as"127.0.0.1:5432:5432". -
Consider a reverse proxy. The broker serves plain HTTP on
1056by 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.
If it goes wrong
Troubleshooting covers the rest of a first run.