Skip to content

Repository files navigation

Timeless

All-in-One Observability Container

License


"I found it ironic that the first thing you do to time series data is squash the timestamp. That's how the name Timeless was born." --Mark Cotner

An all-in-one observability container that bundles TimelessMetrics, TimelessLogs, TimelessTraces, and TimelessUI into a single deployable image.

Ports

Port Service
5556 TimelessUI (Phoenix web dashboard)
8428 TimelessMetrics (Prometheus-compatible HTTP)
9428 TimelessLogs (log ingestion HTTP)
10428 TimelessTraces (OTLP trace ingestion HTTP)

Quick Start

Container (recommended)

timeless_secret_key_base="$(openssl rand -base64 64)"

podman pull ghcr.io/awksedgreep/timeless-stack:latest

podman run -d \
  --name timeless-stack \
  -e SECRET_KEY_BASE="$timeless_secret_key_base" \
  -p 5556:5556 \
  -p 8428:8428 \
  -p 9428:9428 \
  -p 10428:10428 \
  -v timeless_data:/data \
  --health-cmd "curl -fsS http://localhost:8428/live && curl -fsS http://localhost:9428/live && curl -fsS http://localhost:10428/live && curl -fsS http://localhost:5556 >/dev/null" \
  --health-interval 30s \
  --health-timeout 5s \
  --health-start-period 15s \
  --health-retries 3 \
  ghcr.io/awksedgreep/timeless-stack:latest

All data is stored under /data (metrics, logs, traces, and the UI database). Mount a volume to persist across restarts.

Performance note: with rootless podman, published ports go through pasta user-mode forwarding, which costs ~13–18% of peak ingest throughput at saturation (measured 2026-07-18, timeless_metrics cardinality bank). For ingest-heavy deployments use --network=host (drop the -p flags — the stack binds 5556/8428/9428/10428 directly). Under sustained heavy ingest also set a generous stop timeout (e.g. --stop-timeout 120 or TimeoutStopSec= in Quadlets) so the shutdown flush completes. The health probe is configured by Podman at container creation because the OCI image format intentionally has no image-level health-check field.

From Source

TimelessStack requires three sibling repos checked out side-by-side:

parent/
  timeless_stack/
  timeless_metrics/
  timeless_ui/
git clone https://github.com/awksedgreep/timeless_stack.git
git clone https://github.com/awksedgreep/timeless_metrics.git
git clone https://github.com/awksedgreep/timeless_ui.git

cd timeless_stack
mix deps.get
mix assets.setup
mix assets.deploy
mix phx.server

Configuration

All services are configured in config/config.exs. Key settings:

# Data directories (default to /data/* in container)
config :timeless_metrics, data_dir: "/data/metrics", port: 8428
config :timeless_logs, storage: :disk, data_dir: "/data/logs", http: [port: 9428]
config :timeless_traces, storage: :disk, data_dir: "/data/traces", http: [port: 10428]

Building the Container Locally

From the parent directory containing this repo (the data plane is downloaded from the named timeless-libsql release, not built from source — the tag must have published artifacts):

podman build --ignorefile timeless_stack/.containerignore \
  -t timeless-stack -f timeless_stack/Containerfile \
  --build-arg TARGETARCH=amd64 \
  --build-arg TIMELESS_BUILD_COMMIT=2a6a3c994917e704fff31c78f29b108c08b3da29 \
  --build-arg TIMELESS_BUILD_RELEASE=v0.8.2 .

Architecture

TimelessStack is a thin orchestration layer. The default production data plane is three supervised, signal-specific Rust API processes backed by the public timeless-libsql extension. Phoenix remains the control plane and the legacy embedded engines remain available only through the documented offline rollback window.

The exact supported API/query surface, limits, ownership boundary, and rollback procedure are frozen in docs/telemetry_data_plane_compatibility.md.

The bundled components are:

  • TimelessMetrics -- Prometheus-compatible time-series storage and extension-owned rollups in libSQL.
  • TimelessLogs -- Structured rich-log storage in the libSQL block virtual table.
  • TimelessTraces -- OpenTelemetry-compatible rich spans in the libSQL trace virtual table.
  • TimelessUI -- Phoenix LiveView dashboard with real-time canvas visualization, alerting, and metric/log/trace exploration.

Data Retention

TimelessStack ships with longer retention defaults suited for a dedicated observability server.

Engine Default Retention Size Limit
Metrics (raw) 90 days none
Metrics (daily rollup) 365 days none
Logs 90 days 2 GB
Traces 90 days 1 GB

Customizing retention

Override in config/config.exs or config/runtime.exs:

config :timeless_logs,
  retention_max_age: 180 * 86_400,         # 180 days
  retention_max_size: 4_294_967_296,       # 4 GB cap (nil = unlimited)
  retention_check_interval: 300_000        # check every 5 minutes

config :timeless_traces,
  retention_max_age: 30 * 86_400,          # 30 days
  retention_max_size: 2_147_483_648        # 2 GB cap

For metrics:

config :timeless_metrics,
  raw_retention_seconds: 180 * 86_400,     # 180 days raw
  daily_retention_seconds: 730 * 86_400    # 2 years rolled up

Setting retention_max_age to nil disables time-based retention. Setting retention_max_size to nil disables size-based retention.

Environment variables can also be used at runtime (see config/runtime.exs):

LOGS_RETENTION_AGE=7776000    # 90 days in seconds
LOGS_RETENTION_SIZE=2147483648
TRACES_RETENTION_AGE=7776000
TRACES_RETENTION_SIZE=1073741824

Health Checks

Each ingestion service exposes a /health endpoint:

curl http://localhost:8428/health   # metrics
curl http://localhost:9428/health   # logs
curl http://localhost:10428/health  # traces

License

MIT -- see LICENSE for details.

About

Standalone observability stack composing TimelessMetrics, TimelessLogs, and TimelessTraces into a single BEAM application for container deployment

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages