Skip to content

Managing Dependencies

Your apps can use any Python package. Hassette installs them at startup when you tell it to. requirements.txt works for most projects. pyproject.toml works when your project already has one.

Using requirements.txt

apprise>=1.9
astral>=3.2

Place this file at config/requirements.txt on your host. That maps to /config/requirements.txt inside the container.

Add HASSETTE__INSTALL_DEPS: "1" to your compose file:

services:
  hassette:
    image: ghcr.io/nodejsmith/hassette:latest-py3.13
    environment:
      HASSETTE__INSTALL_DEPS: "1"
    volumes:
      - ./config:/config
      - ./apps:/apps
      - data:/data
      - uv_cache:/uv_cache

volumes:
  uv_cache:
  data:

Without HASSETTE__INSTALL_DEPS, Hassette skips installation entirely. The uv_cache volume keeps downloaded packages across restarts. Only the first startup is slow.

Restart the container — the install runs during startup, and you can watch it with docker compose logs -f hassette. Your packages are then available:

import apprise
from hassette import App, AppConfig


class NotifyConfig(AppConfig):
    notify_url: str = "tgram://bot_token/chat_id"


class NotifyApp(App[NotifyConfig]):
    async def on_initialize(self) -> None:
        self.notifier = apprise.Apprise()
        self.notifier.add(self.app_config.notify_url)
        self.logger.info("Notification service ready")

The app imports apprise directly. No extra configuration needed. (The # pyright: ignore comment in the example quiets an editor warning when the package isn't installed on your local machine — your own code doesn't need it.)

Tip

After adding new packages to requirements.txt, restart the container with docker compose restart hassette. Hassette re-runs the install on every startup when HASSETTE__INSTALL_DEPS is set.

Using pyproject.toml

[project]
name = "my-hassette-apps"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
    "apprise>=1.9",
    "astral>=3.2",
]

If you already have a pyproject.toml, place it in your apps/ directory alongside your app files. You also need a uv.lock next to it — a file recording the exact version of every package, so the container installs the same versions you tested locally. Generate one by running this in your apps/ directory before starting the container:

uv lock

Your compose file stays the same as the Docker Setup page. No extra environment variables are needed:

services:
  hassette:
    image: ghcr.io/nodejsmith/hassette:latest-py3.13
    volumes:
      - ./config:/config
      - ./apps:/apps
      - data:/data
      - uv_cache:/uv_cache

volumes:
  uv_cache:
  data:

Hassette checks /apps for a uv.lock on startup. If it finds one, it installs the locked dependencies automatically. HASSETTE__INSTALL_DEPS is not needed.

If your pyproject.toml lives somewhere other than apps/, set HASSETTE__PROJECT_DIR to point Hassette at it. Add the variable to your compose environment and mount the directory.

Hassette pins its own dependencies via a constraints file. Your packages cannot conflict with packages Hassette depends on. If a conflict occurs, the install fails at startup — see Troubleshooting.

Note

Commit uv.lock to version control. Hassette uses it to reproduce the exact package versions you tested locally.

Known Limitations

Local path dependencies don't work inside Docker. If your pyproject.toml or requirements.txt contains a file:///... dependency, installation fails because the host path does not exist inside the container. Mount the shared code as a volume with a relative path that matches the container layout, or publish it as a package.

First startup is slower with new dependencies. Hassette runs uv pip install on every start when HASSETTE__INSTALL_DEPS is set. New packages download on the first run. The uv_cache volume persists the cache, so subsequent starts skip the download. If your cache volume is missing or was pruned, the next startup downloads everything again.

If installation fails at startup, see Troubleshooting for common causes and fixes.