A containerized development environment for Klipper firmware development with integrated debugging support for STM32-based boards.
This setup provides a complete Klipper development environment running in Docker containers, with the host system managing the hardware connections (USB serial devices and JTAG debugger). The configuration includes:
- Klipper: Main firmware service running in a custom container
- Moonraker: API server for printer management
- Mainsail: Web-based user interface
- VSCode Integration: Configured workspace with build tasks and debugging
Host System
├── USB Serial Device (STM32 Board)
├── J-Link Debugger
└── Docker Containers
├── Klipper Service (custom build)
├── Moonraker (mkuf/moonraker:latest)
└── Mainsail Web UI (ghcr.io/mainsail-crew/mainsail:latest)
- Docker and Docker Compose
- Linux host with USB serial device support
- (Optional) J-Link debugger for firmware debugging
- (Optional) VSCode for integrated development
.
├── docker-compose.yml # Container orchestration
├── Dockerfile # Custom Klipper container
├── klipper/ # Klipper source code (git submodule)
├── katapult/ # Katapult bootloader source
├── config/ # Configuration files
│ ├── printer.cfg # Klipper printer configuration
│ ├── moonraker.conf # Moonraker API configuration
│ ├── mainsail-config.json # Mainsail UI settings
│ └── nginx-mainsail.conf # Nginx configuration
├── logs/ # Runtime logs
└── gcodes/ # G-code files storage
git clone https://github.com/Klipper3d/klipper.git klipper
git clone https://github.com/Arksine/katapult.git katapultdocker compose up -dThis will:
- Build the custom Klipper container with Python dependencies
- Start Moonraker API server
- Start Mainsail web interface
- Create shared volumes for configuration and data
- Mainsail UI: http://localhost:8010
- Moonraker API: http://localhost:7125
Katapult is a bootloader that enables firmware updates over USB without requiring physical access to the SD card or boot pins. This is highly recommended for development workflows.
- Configure Katapult:
cd katapult
make menuconfigConfigure for STM32F103 (SKR Mini v1.3):
- Microcontroller: STM32
- Processor: STM32F103
- Clock Reference: 8 MHz crystal
- Bootloader Size: 8KiB
- Communication: USB (PA11/PA12)
- Status LED: Optional (e.g., PC13)
- Build Bootloader:
make clean
makeOutput: out/katapult.bin
- Flash Bootloader (one-time setup):
# Method 1: Copy to SD card as firmware.bin and power cycle
cp out/katapult.bin /path/to/sdcard/firmware.bin
# Method 2: Use J-Link
# (Connect J-Link to SWD pins on board)
JLinkExe -device STM32F103RC -if SWD -speed 4000
J-Link> connect
J-Link> loadfile out/katapult.bin 0x08000000
J-Link> exit
# Method 3: Use ST-Link
st-flash write out/katapult.bin 0x08000000- Verify Bootloader:
After flashing Katapult, the board will appear as a USB device with idVendor=1d50, idProduct=6177:
# Check dmesg for Katapult device
dmesg | grep katapult
# Output should show:
# usb X-X: Product: stm32f103xe
# usb X-X: Manufacturer: katapultWhen using Katapult bootloader, Klipper must be configured with the correct offset.
- Configure Build:
cd klipper
make menuconfigConfigure for your board with Katapult:
- Microcontroller: STM32
- Processor: STM32F103
- Bootloader offset: 8KiB (matches Katapult size)
- Clock Reference: 8 MHz crystal
- Communication: USB (PA11/PA12)
- Build Firmware:
make clean
makeOutput: out/klipper.bin
- Flash to Board:
With Katapult installed, you can flash Klipper over USB:
# Flash using Katapult's flashtool
../katapult/scripts/flashtool.py -d /dev/ttyACM0 -f out/klipper.binThe board will:
- Start in Katapult bootloader mode (idProduct=6177)
- Receive firmware update
- Reboot into Klipper (idProduct=614e)
Verify Klipper is running:
dmesg | tail -20
# Should show:
# usb X-X: Product: stm32f103xe
# usb X-X: Manufacturer: Klipper
# usb X-X: SerialNumber: 35FFD8054E4B323817761243Open the workspace file klipper.code-workspace for integrated development with preconfigured build and flash tasks.
Available Tasks (Ctrl+Shift+P → "Tasks: Run Task"):
Klipper Firmware:
- Build Firmware: Compile Klipper firmware
- Clean Build: Clean and rebuild Klipper
- Restart Klipper Service: Restart Klipper Docker container
- Build and Restart: Build firmware and restart Klipper service
Debug Configurations (F5 or Run and Debug):
- Run FW (J-Link): Debug Klipper firmware on MCU via J-Link with live watch enabled
The container requires access to serial devices. Permissions are handled via:
- Device cgroup rules in
docker-compose.yml - User group membership: Container user added to
dialoutgroup - Volume mounts: Serial devices mounted from host
Verify device availability:
docker compose exec klipper ls -l /dev/serial/by-id/Edit config/printer.cfg to match your hardware. Key sections:
[mcu]: Serial device path[stepper_*]: Motor pin assignments and kinematics[extruder]: Hotend configuration[heater_bed]: Heated bed settings
After changes, restart the service:
docker compose restart klipper# View all services
docker compose logs -f
# Specific service
docker compose logs -f klipper
docker compose logs -f moonraker- Check device exists:
ls -l /dev/serial/by-id/- Verify permissions:
groups # Should include 'dialout'- Test connection:
docker compose exec klipper ls -l /dev/serial/by-id/- Check configuration syntax:
docker compose exec klipper /opt/venv/bin/python /opt/klipper/klippy/klippy.py --check-config /opt/printer_data/config/printer.cfg- Review logs:
tail -f logs/klippy.logVerify socket communication:
docker compose exec moonraker ls -l /tmp/klipper/klippy.sockContainer resource limits are configured in docker-compose.yml:
- Klipper: 1 CPU, 512MB RAM
- Moonraker: 1 CPU, 512MB RAM
- Mainsail: 0.5 CPU, 256MB RAM
Adjust these based on your host system capabilities.
- Hot Reload: Klipper Python code changes require service restart
- Firmware Changes: With Katapult, flash via USB using
flashtool.py. Without Katapult, rebuild and reflash to MCU via SD card or programmer - Config Changes: Restart Klipper service or use Mainsail UI restart
- Keep Logs: Logs persist in
./logs/directory for debugging - USB Device States:
- Katapult mode: idProduct=6177 (ready for firmware update)
- Klipper mode: idProduct=614e (normal operation)
- Quick Firmware Update:
cd klipper && make && ../katapult/scripts/flashtool.py -d /dev/ttyACM0 -f out/klipper.bin
All services use network_mode: host for simplicity. Ports used:
- 7125: Moonraker API
- 8010: Mainsail web interface
For remote access, configure firewall rules or use reverse proxy.
When modifying Klipper source:
- Work in the
klipper/directory - Follow Klipper coding standards (80-character line limit)
- Test changes with your hardware
- Submit pull requests to upstream Klipper repository
This development environment configuration is provided as-is. Klipper itself is licensed under GPL v3.