Skip to content

Running with Your App

Dachshund runs inside your application's process. Call dachshund.init() once, where your application starts, and dachshund.shutdown() where it stops. Pages can be included before or after init().

Options

dachshund.init(
    "My service",
    description="What my service does",
    host="127.0.0.1",
    port=1212,
    path="dachshund.db",
    storage=None,
    context={},
    authorize=None,
    login_url=None,
    timezone="UTC",
)
Option What it sets
name, description The title and subtitle at the top of every page.
host, port Where the dashboard listens. Defaults to 127.0.0.1:1212.
path The SQLite file events are kept in. ":memory:" keeps them out of any file.
storage Where persisted widget state is saved, see Persistent Storage.
context Passed as ctx to every action and table loader, see Actions.
authorize, login_url Who may see each page and run each action, see Authentication.
timezone Where "day", "week" and "month" windows and daily buckets start, see Aggregating. Defaults to "UTC".

path and storage only apply to the first call that touches the dashboard. Call init() before adding widgets or including pages so they're used.

A script

The dashboard runs on a background thread that ends with the process. A script has to keep running for the dashboard to stay up.

import time

import dachshund

dachshund.init("My script")
dachshund.include(overview_page)

while True:
    dachshund.emit("queue_size", value=queue.size())
    time.sleep(1)

FastAPI

Start it in the app's lifespan:

from contextlib import asynccontextmanager

import dachshund
from fastapi import FastAPI


@asynccontextmanager
async def lifespan(app: FastAPI):
    dachshund.init("My API")
    dachshund.include(overview_page)
    yield
    dachshund.shutdown()


app = FastAPI(lifespan=lifespan)

init() runs inside the app's event loop here, so async actions run on that loop too.

Flask

Start it in the application factory:

import dachshund
from flask import Flask


def create_app() -> Flask:
    app = Flask(__name__)

    dachshund.init("My app")
    dachshund.include(overview_page)

    return app

An asyncio application

Call init() from inside the running loop, so async actions run on it and can await your application's clients:

import asyncio

import dachshund


async def main() -> None:
    dachshund.init("My service")
    dachshund.include(overview_page)

    await serve_forever()


asyncio.run(main())

Several workers

Under a server running several worker processes, such as gunicorn or uvicorn with --workers, every worker calls init(), but only one can hold the dashboard's port. The others stand by and take over if it stops.

Every worker writes its events to the same events file, so a page shows all of them when it loads. Live updates only come from the worker serving the dashboard, so events from the others show on the next page load. Persisted widget state isn't merged between workers.

Exposing the dashboard

Danger

The dashboard has no authentication by default. Anyone who can reach its port sees every page and can run every action. Keep it on 127.0.0.1, or give it an authorize function, see Authentication.

To listen on every interface, for example inside a container:

dachshund.init("My service", host="0.0.0.0", port=1212)

A reverse proxy must allow WebSocket upgrades on /_ws, which carries the live updates.