Skip to content

Docker Installation

Docker is the easiest way to run Bambuddy. One command and you're done!


🚀 Quick Start

Interactive script that prompts for configuration and sets everything up. Requires a bash-compatible shell — Linux, macOS, or WSL on Windows. For native Windows, use the Pre-built Image tab below.

curl -fsSL https://raw.githubusercontent.com/maziggy/bambuddy/main/install/docker-install.sh -o docker-install.sh && chmod +x docker-install.sh && ./docker-install.sh

The script will:

  • Prompt for install path, port, bind address, timezone
  • Download docker-compose.yml (or clone repo if building from source)
  • Create .env file with your settings
  • Start the container

Unattended Mode

For automation or CI, use the --yes flag to accept all defaults:

curl -fsSL https://raw.githubusercontent.com/maziggy/bambuddy/main/install/docker-install.sh | bash -s -- --yes

Or customize with flags:

curl -fsSL https://raw.githubusercontent.com/maziggy/bambuddy/main/install/docker-install.sh | bash -s -- --path /srv/bambuddy --port 3000 --yes

The fastest way — no building required. Works on Linux, macOS, and native Windows PowerShell (no WSL needed):

# Linux / macOS / WSL
mkdir bambuddy && cd bambuddy
curl -O https://raw.githubusercontent.com/maziggy/bambuddy/main/docker-compose.yml
docker compose up -d
# Native Windows PowerShell
mkdir bambuddy
cd bambuddy
Invoke-WebRequest -Uri https://raw.githubusercontent.com/maziggy/bambuddy/main/docker-compose.yml -OutFile docker-compose.yml
docker compose up -d

Multi-Architecture Support

Pre-built images are available for:

  • linux/amd64 - Intel/AMD servers, desktops, most VPS
  • linux/arm64 - Raspberry Pi ⅘, Apple Silicon Macs, AWS Graviton

Docker automatically pulls the correct image for your system.

If you want to build locally or modify the code:

git clone https://github.com/maziggy/bambuddy.git
cd bambuddy
docker compose up -d --build

Open http://<your-host>:8000 in your browser — replace <your-host> with the IP or hostname of the machine running Bambuddy. On Linux with host networking localhost works too; on Docker Desktop (macOS/Windows) it does not — see the macOS and Windows section below. 🎉


Configuration

docker-compose.yml

The default docker-compose.yml works out of the box:

services:
  bambuddy:
    image: ghcr.io/maziggy/bambuddy:latest
    build: .
    # Usage:
    #   docker compose up -d          → pulls pre-built image
    #   docker compose up -d --build  → builds from source
    container_name: bambuddy
    network_mode: host  # Recommended for automatic printer discovery
    volumes:
      - bambuddy_data:/app/data
      - bambuddy_logs:/app/logs
    environment:
      - TZ=Europe/Berlin  # Your timezone
    restart: unless-stopped

volumes:
  bambuddy_data:
  bambuddy_logs:

Image vs Build

When both image and build are specified:

  • docker compose up -d pulls the pre-built image from ghcr.io
  • docker compose up -d --build builds locally from source

Environment Variables

Variable Default Description
TZ UTC Your timezone (e.g., America/New_York)
PORT 8000 Port Bambuddy runs on (with host networking mode)
DEBUG false Enable debug logging
LOG_LEVEL INFO Log level: DEBUG, INFO, WARNING, ERROR
HA_URL (none) Home Assistant URL for automatic integration (e.g., http://192.168.1.100:8123)
HA_TOKEN (none) Home Assistant Long-Lived Access Token for automatic integration
TRUSTED_FRAME_ORIGINS (none) Comma-separated origins permitted to embed Bambuddy via <iframe> (e.g., http://homeassistant.local:8123). Required for the HA Webpage dashboard panel.
DATABASE_URL (none) External PostgreSQL connection string (e.g., postgresql+asyncpg://user:pass@host:5432/bambuddy). Uses built-in SQLite when not set.
USE_SYSTEM_TRUST_STORE (none) Enables the use of the System Trust Store for HTTPS requests
BAMBUDDY_EXTERNAL_ROOTS (none) Colon-separated absolute paths permitted as external library folders (see File Manager → External folders). Empty default disables the feature; Bambuddy's own data / log / static directories are always rejected even if the operator over-broadens this list.

Home Assistant Integration

When both HA_URL and HA_TOKEN are set, the Home Assistant integration is automatically enabled and configured. The URL and token fields become read-only in the UI. This is primarily used by the Home Assistant add-on for zero-configuration setup.

Self Signed CA Certificate Support

By default Bambuddy uses the built in httpx library trust store for all HTTPS requests. If you have a standalone Home Assistant instance and are using a self signed CA certificate then access to this instance will be denied. To include your own CA certifcate add it to a directory and add the following to your docker compose file:

volume:
  - /path/to/your/ca-cert-dir:/usr/local/share/ca-certificates:ro
environment:
  - USE_SYSTEM_TRUST_STORE=true

When the USE_SYSTEM_TRUST_STORE environment variable is defined Bambuddy will update the CA Certifcates at boot up and enable the httpx library to use the System Trust Store. USE_SYSTEM_TRUST_STORE only takes effect when the container starts as root; remove any explicit user: directive if you set this flag.

Embedding Bambuddy in Home Assistant's Webpage panel

By default, Bambuddy emits strict iframe-blocking headers (X-Frame-Options: SAMEORIGIN and CSP frame-ancestors 'none') to protect against clickjacking on internet-exposed deployments. This blocks embedding Bambuddy inside Home Assistant's Webpage dashboard panel even on a trusted LAN, because HA on port 8123 and Bambuddy on port 8000 are different origins to the browser.

To allow embedding from your HA instance, set:

environment:
  - TRUSTED_FRAME_ORIGINS=http://homeassistant.local:8123

Replace the URL with your actual HA origin (scheme://host[:port] only — no paths, no wildcards). Multiple origins can be comma-separated. When set, X-Frame-Options is removed and the CSP frame-ancestors directive lists 'self' plus your configured origins. Plain Docker / bare-metal deployments without this variable retain the strict default.

HTTPS Home Assistant (Nabu Casa, custom domain, anything with TLS in front): browsers refuse to embed an HTTP iframe inside an HTTPS page (mixed-content block — Chrome and Firefox enforce this and the user can't override it for individual sites). If your HA instance is HTTPS, Bambuddy must also be reachable over HTTPS.

The simplest recipe for HA users is the Nginx Proxy Manager addon from the HA addon store. NPM does DNS-01 Let's Encrypt against your own domain, so no port-forwarding is required and Bambuddy never has to be exposed publicly:

  1. Install and start the Nginx Proxy Manager addon from the HA addon store.
  2. In NPM, add a Proxy Host: domain bambuddy.<your-domain>, scheme http, forward host <bambuddy-host-on-LAN>, forward port 8000.
  3. Under SSL, request a Let's Encrypt cert via DNS Challenge for bambuddy.<your-domain> — NPM has dropdowns for Cloudflare, Hetzner, Route53, and many other DNS providers.
  4. Set TRUSTED_FRAME_ORIGINS=https://homeassistant.<your-domain> (or whatever your HA HTTPS origin is) on Bambuddy and restart.
  5. In HA, add a Webpage dashboard panel with url: https://bambuddy.<your-domain>.

Both ends are HTTPS, the certificate is publicly trusted, and Bambuddy stays on the LAN — DNS-01 only needs API access to your DNS provider, not inbound connectivity. If you don't have a domain, any other reverse proxy works the same way (Caddy, Traefik, plain nginx outside the addon ecosystem) — the only requirement is that Bambuddy ends up behind a trusted HTTPS certificate that matches the URL you embed in the Webpage panel.

Path-prefixed reverse proxies (e.g. https://example.com/bambuddy/ via Traefik path prefix, nginx location /bambuddy/, Cloudflare Tunnel with path routing) are supported as of v0.2.4b2 — assets load correctly under any subpath. API calls still go to the host root, so the proxy must route /api/v1/* to the same Bambuddy upstream as /bambuddy/* (most users either reverse-proxy Bambuddy at a dedicated subdomain or expose /api/v1/ at the host root alongside the subpath).

HA Ingress / addon-based subpath embedding is not supported

Home Assistant's add-on Ingress system serves the addon at a rotating per-session subpath (/api/hassio_ingress/<token>/). Even though the asset-path fix above lets the SPA boot under that prefix, the rest of the SPA still assumes a stable origin: API calls, React Router basename, PWA manifest scope, service-worker scope, and push-notification subscriptions are all anchored at the URL the SPA was first installed under. Making each of those subpath-aware would mean rewriting how the SPA bootstraps and would create new failure modes around PWA installs and deep-link reloads.

The supported HA embedding path is the Webpage panel + TRUSTED_FRAME_ORIGINS flow above. Note that terminating TLS inside an HA addon container with a self-signed certificate is not a viable workaround: modern browsers (Chrome, Edge) block self-signed certificates on raw LAN IPs with no thisisunsafe-style override available. If you maintain an HA add-on that wraps Bambuddy, the practical path is to have the user front Bambuddy with the Nginx Proxy Manager addon (or any other reverse proxy that produces a publicly trusted certificate via DNS-01), then embed Bambuddy's HTTPS URL via Webpage panel.

External PostgreSQL Database

By default, Bambuddy uses a built-in SQLite database that requires zero configuration. For larger setups or when you prefer a dedicated database server, set DATABASE_URL to point to an external PostgreSQL instance:

environment:
  - DATABASE_URL=postgresql+asyncpg://bambuddy:yourpassword@db-host:5432/bambuddy

Bambuddy will automatically create all tables on first startup. Backup/restore uses pg_dump/pg_restore instead of file copy.

External library folders (BAMBUDDY_EXTERNAL_ROOTS)

The File Manager → Add external folder feature lets users mount host directories (NAS shares, USB drives, local prints directories) into the library without copying files. As of v0.2.5b1 this is opt-in for operators — set BAMBUDDY_EXTERNAL_ROOTS to a colon-separated list of host paths (inside the container) that users are permitted to register. Empty (the default) disables the feature.

Operators must also bind-mount the host directory into the container at the same path declared in BAMBUDDY_EXTERNAL_ROOTS. The Bambuddy data / log / static directories cannot appear as external roots even if you list them here — they are always rejected as a hard safeguard against cross-user data exposure.

Example: allow users to mount one NAS share read-only:

volumes:
  - /mnt/nas/3d-prints:/external/nas:ro
environment:
  - BAMBUDDY_EXTERNAL_ROOTS=/external/nas

Example: allow two roots (NAS + local projects), comma-separated host paths but colon-separated in-container:

volumes:
  - /mnt/nas/3d-prints:/external/nas:ro
  - /srv/library:/external/projects:ro
environment:
  - BAMBUDDY_EXTERNAL_ROOTS=/external/nas:/external/projects

:ro is recommended unless you want users uploading files back into the host share. Why operator-opt-in? See the security advisory note — pre-v0.2.5b1 versions used a denylist of system paths which left every other host location implicitly mountable. The allowlist makes mounting an explicit, auditable decision per deployment.

Custom Port

With network_mode: host, set the PORT environment variable:

PORT=8080 docker compose up -d

Or add it to your docker-compose.yml:

environment:
  - TZ=Europe/Berlin
  - PORT=8080

With port mapping, modify the ports section:

ports:
  - "8080:8000"  # Access on port 8080

Linux Users: Permission Denied?

If you get "permission denied" errors, either prefix commands with sudo (e.g., sudo docker compose up -d) or add your user to the docker group.


Data Persistence

Three volumes store your data:

Volume Purpose
bambuddy.db SQLite database with all your print data
archive/ Archived 3MF files and thumbnails
logs/ Application logs

Backup

To backup your data, simply copy these files/directories. See Backup & Restore for the built-in backup feature.


Updating

In-App Updates Not Available

Docker installations cannot use the in-app update feature — upgrade from the command line.

Check your image: line first

If your docker-compose.yml pins a specific tag (e.g. ghcr.io/maziggy/bambuddy:0.2.2.2), docker compose pull will just re-fetch that same tag. Edit the line to :latest or the target version (e.g. :0.2.3) before pulling.

docker compose pull
docker compose up -d
cd bambuddy
git pull
docker compose build --pull
docker compose up -d

The --pull flag ensures you get the latest base image with security updates.

Stale docker-compose.yml?

Releases since 0.2.2 added cap_add: NET_BIND_SERVICE, extra virtual-printer ports for bridge mode (2024-2026), and an optional PostgreSQL block. If your compose file is older, bridge-mode users (macOS/Windows) may silently lose FTP and RTSP proxies. Compare yours against the current file and merge by hand.


Useful Commands

View Logs

# Follow logs
docker compose logs -f

# Last 100 lines
docker compose logs --tail 100

Stop/Start

# Stop
docker compose down

# Start
docker compose up -d

Rebuild (after changes)

docker compose up -d --build

Shell Access

docker compose exec bambuddy /bin/bash

Advanced Setups

Reverse Proxy (Nginx / Caddy)

See Installation → Reverse Proxy — the Nginx and Caddy configs there work the same for Docker deployments. Just point the upstream at the Bambuddy container (bambuddy:8000 on the same compose network, or localhost:8000 if the proxy runs on the host).

Traefik Labels

If you're using Traefik:

services:
  bambuddy:
    build: .
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.bambuddy.rule=Host(`bambuddy.yourdomain.com`)"
      - "traefik.http.routers.bambuddy.entrypoints=websecure"
      - "traefik.http.routers.bambuddy.tls.certresolver=letsencrypt"
    # ... rest of config

Network Mode Host

Host network mode is required for printer discovery and camera streaming:

services:
  bambuddy:
    build: .
    network_mode: host
    # Note: ports mapping not needed with host mode

Required for Printer Discovery

Docker's default bridge networking cannot receive SSDP multicast packets needed for automatic printer discovery. You must use network_mode: host for discovery to work.

When Using Host Mode

  • Remove the ports: section (not needed with host mode)
  • Bambuddy will be accessible on port 8000 directly
  • All other features work the same

Host mode not an option? Bridge works too

Host mode is the default because SSDP printer discovery needs L2 multicast — but it's a deliberate trade-off, not a one-size-fits-all default. If you run Bambuddy on a multi-service Linux host (NAS, dedicated Docker VM, Synology DSM, Unraid) where another container already binds a port Bambuddy wants, host mode isn't an option for you. Bridge mode is a fully supported alternative for setups that add printers by IP and skip discovery — read the Bridge Mode section below for the port-exposure rules. Both Linux multi-service hosts and Docker Desktop (macOS / Windows) follow the same configuration there.

Bridge Mode (Linux and Docker Desktop)

Bridge mode is required on any setup that can't use network_mode: host. That covers two distinct audiences with the same configuration:

  • Linux multi-service hosts (NAS, dedicated Docker VM, Synology DSM, Unraid, shared homelab box) where host mode would conflict with ports already bound by other containers.
  • Docker Desktop on macOS / Windows, where host mode connects to the Linux VM rather than your computer's network (more on the macOS/Windows specifics below).

Both cases lose automatic SSDP printer discovery — add printers manually by IP instead. The compose snippet for both is the same:

services:
  bambuddy:
    image: ghcr.io/maziggy/bambuddy:latest
    container_name: bambuddy
    cap_add:
      - NET_BIND_SERVICE
    ports:
      - "${PORT:-8000}:8000"           # Web UI
      - "3000:3000"                    # Virtual printer bind/detect
      - "3002:3002"                    # Virtual printer bind/detect (alt port)
      - "990:990"                      # Virtual printer FTPS
      - "8883:8883"                    # Virtual printer MQTT
      - "6000:6000"                    # Virtual printer file transfer tunnel
      - "322:322"                      # Virtual printer RTSP camera (X1/H2/P2)
      - "2024-2026:2024-2026"          # Virtual printer proprietary ports (A1/P1S)
      - "50000-50029:50000-50029"      # Virtual printer FTP passive data — 3 VPs × 10-port slice
    volumes:
      - bambuddy_data:/app/data
      - bambuddy_logs:/app/logs
    environment:
      - TZ=Europe/Berlin
      # Required for virtual printer FTP passive mode behind Docker NAT:
      # Set to your Docker host's LAN IP
      #- VIRTUAL_PRINTER_PASV_ADDRESS=192.168.1.100
    restart: unless-stopped

volumes:
  bambuddy_data:
  bambuddy_logs:

FTP passive ports are sliced per VP — only expose what you need

Each Virtual Printer is assigned its own non-overlapping 10-port passive-mode slice (#1646): VP 1 → 50000-50009, VP 2 → 50010-50019, VP 3 → 50020-50029, and so on. Only expose the range your VPs actually use:

  • 1 VP50000-50009:50000-50009 (10 ports)
  • 3 VPs (default above) → 50000-50029:50000-50029 (30 ports)
  • N VPs50000-500{N-1}9:50000-500{N-1}9 (N × 10 ports)
  • Proxy mode50000-50100:50000-50100 is still required for any VP — proxy mode transparently forwards the real printer's full FTP-data range, not Bambuddy's slice.

Why exposing the minimum matters: with Docker's default "userland-proxy": true, every exposed port spawns one docker-proxy host process per address family (IPv4 + IPv6). A 1001-port range produces ~2000 such processes pinning ~3.5 GB of host RAM that doesn't appear in docker stats (host-level, not container-level). The 30-port default above is ~60 processes / ~210 MB — and a single-VP install is ~20 processes / ~70 MB.

If you can't narrow the exposed range and want a daemon-wide reduction, setting { "userland-proxy": false } in /etc/docker/daemon.json removes the docker-proxy cost across every container on the host — that's a global trade-off, so the per-VP slicing above is the lower-impact lever.

PASV Address

Bridge mode hides the host's real IP from the FTP server inside the container. Set VIRTUAL_PRINTER_PASV_ADDRESS (commented in the snippet above) to your Docker host's LAN IP so the FTP server tells slicers the right address for the passive data connection.

macOS and Windows (Docker Desktop)

Docker Desktop on macOS and Windows runs containers inside a Linux VM, so network_mode: host connects to the VM's network, not your computer's network. This means:

  • Port 8000 won't be accessible via localhost
  • Printer discovery won't work (the container can't see your LAN)

Solution: use the Bridge Mode configuration above — the compose snippet, port-mapping rules, and FTP-passive slicing guidance apply identically here.

Manual Printer Setup Required

On macOS/Windows, you must add printers manually by IP address. Automatic discovery is not available because Docker Desktop cannot access LAN multicast traffic.

Printer Discovery in Docker

Virtual Printer SSDP Discovery

SSDP discovery for the virtual printer (slicer discovering Bambuddy) also requires host networking or same-LAN connectivity. In Docker bridge mode (macOS/Windows), slicers must add the virtual printer manually by IP address.

When running in Docker with network_mode: host, Bambuddy uses subnet scanning instead of SSDP multicast for printer discovery:

  1. Click Add Printer on the Printers page
  2. Bambuddy detects it's running in Docker and shows a subnet input field
  3. Enter your network range (e.g., 192.168.1.0/24)
  4. Click Scan Network - Bambuddy will probe each IP for Bambu printer ports (8883, 990)
  5. Discovered printers appear in the list with their name and model

Finding Your Subnet

Your subnet is typically your IP address with the last number replaced by 0/24. For example:

  • If your computer's IP is 192.168.1.50, use 192.168.1.0/24
  • If your computer's IP is 10.0.0.25, use 10.0.0.0/24

How It Works

Subnet scanning checks each IP address in the range for open ports 8883 (MQTT) and 990 (FTPS). When both ports are open, it sends an SSDP query to get the printer's name, serial number, and model.


Troubleshooting

Container Won't Start

Check the logs:

docker compose logs bambuddy

Common issues:

  • Port in use: Change the port mapping
  • Permission denied: Check volume permissions

Can't Connect to Printer

Ensure your Docker network can reach your printer:

# Test connectivity from inside container
docker compose exec bambuddy ping YOUR_PRINTER_IP

If using bridge network mode and having issues, try network_mode: host.

Database Issues

If you need to reset the database:

docker compose down
rm bambuddy.db
docker compose up -d

Data Loss

This will delete all your print history and settings!


🏁 Next Steps