Skip to main content

Deploying Observable

Observable consists of two main components:

  1. a Go-based backend that monitors network endpoints and,
  2. a Next.js frontend that visualizes the telemetry in a 3D knowledge graph.

Get Podman Images

First, download the pre-built container images from the OpenML artifact repository:

# Download the frontend and backend images
curl -L -O https://observable.openml.io/openml-observable-frontend.tar
curl -L -O https://observable.openml.io/openml-observable-backend.tar

# Load the images into Podman
podman load -i openml-observable-frontend.tar
podman load -i openml-observable-backend.tar

Preparing the Host Storage

Create a directory on the host where the SQLite database will permanently reside. This ensures data persistence across container restarts.

mkdir -p /opt/observable/data

Since we are running Podman in rootless mode, we must ensure the directory is writable by the container process:

# Take ownership of the directory on the host
sudo chown -R $USER:$USER /opt/observable/data

# Ensure the directory is writable
chmod 775 /opt/observable/data

Backing Up SQLite Data

Never perform a standard file copy (like cp observable.db observable_backup.db) while the Go daemon is actively running. Because our service is actively writing to the database every 30 seconds, copying the raw file mid-transaction can result in a corrupted, unrecoverable backup.

Instead, we must use SQLite's native, transaction-safe backup prodecure below:

  1. Mounting a persistent volume, like we did above

  2. Using the native backup command, sqlite3, which cleanly reads the database state and writes a consistent snapshot to a new file, pausing briefly if a write transaction is occurring to avoid corrupting the active daemon's transactions:

    sqlite3 /opt/observable/data/observable.db ".backup /opt/observable/data/observable_backup_$(date +%F).db"

    A backup file will appear under /opt/observable/data/ directory. For example:

    $ ls /opt/observable/data
    observable.db observable_backup_2026-06-03.db

    where observable_backup_2026-06-03.db is the backup we just created

  3. Automating backup with a host-level cron job, which ensures our time-series data is protected. Open host's crontab using

    crontab -e

    and add a schedule like this to run it every night at 2:00 AM:

    0 2 * * * sqlite3 /opt/observable/data/observable.db ".backup /opt/observable/data/observable_backup_$(date +\%F).db"

Deploying with Podman Compose

The most efficient way to run both services together is using a Compose file. Create a file named compose.yaml in our deployment directory:

# compose.yaml
services:
backend:
image: openml-observable-backend
container_name: observable-backend
restart: always
ports:
- "8080:8080"
volumes:
- /opt/observable/data:/app/data:Z

frontend:
image: openml-observable-frontend
container_name: observable-frontend
restart: always
ports:
- "3000:3000"
depends_on:
- backend

Launch the stack using:

podman-compose up -d

Verification

Once the stack is running, you can verify the deployment by accessing the following endpoints:

  • Frontend HUD: http://localhost:3000
  • Backend Status API: http://localhost:8080/status

To check the logs of the running services:

podman-compose logs -f