Understanding A Typical Klipper Setup - Klipper, Moonraker, Mainsail/Fluidd, Nginx, Camera

May 2, 20244 minutes

Exploring a typical software stack for a Klipper 3D Printer.

Overview

The software stack for a typical Klipper setup encompasses several key components. First is Klipper itself, serving as the 3D printer firmware. Then there’s Moonraker, functioning as the web API layer. Additionally, there’s a client application, such as Mainsail or Fluidd. Finally, there’s typically a camera service that provides the video stream.

Klipper operates locally and exposes its interface using a Unix Domain Socket. To extend this interface over the network, Moonraker connects to it and facilitates communication through a defined HTTP(S)/Websocket protocol. This setup enables any HTTP server to host a WebUI for printer control, including options like nginx, lighttpd, apache, or even python’s built-in HTTP server module. Since camera services usually operate their own servers, their streams are readily accessible over the network. With these combined services, users gain a modern and powerful means to manage their printers locally.

Let’s show this as a sequence diagram:

diagram

When Klipper starts, the Klippy service creates a Unix Domain Socket in /tmp/klippy_uds for communication. Moonraker connects and communicates with Klippy over this socket.

KlippyConnection
class KlippyConnection:
    def __init__(self, config: ConfigHelper) -> None:
        self.server = config.get_server()
        self.uds_address = config.getpath(
            "klippy_uds_address", pathlib.Path("/tmp/klippy_uds")
        )
        ...

Moonraker also enables various controls over its WebAPI, e.g. managing the host services, reboot, and etc.

If webcam stream is needed, then running ustreamer will expose a stream on port 8080. With Moonraker, Klipper, and ustreamer running, serving Mainsail asssets with a HTTP server like nginx will complete the asks. Taking this a step futher, the above diagram has nginx acting as a reverse proxy (on top of serving the Mainsail assets). As a reverse proxy, nginx acts as a middleware and relays communcations between Mainsail and the proxied services (Moonraker/ustreamer).

The nginx configuration would look something like this:

nginx.conf
 1http {
 2  # enable websocket proxying
 3  map $http_upgrade $connection_upgrade {
 4    default upgrade;
 5    '' close;
 6  }
 7
 8  server {
 9    listen 80 default_server;
10    ...
11
12    # mainsail static assets
13    root /home/pi/mainsail;
14
15    index index.html;
16    server_name _;
17
18    # disable proxy request buffering
19    proxy_request_buffering off;
20
21    # root serves index.html
22    location / {
23        try_files $uri $uri/ /index.html;
24    }
25
26    # proxy /websocket to moonraker (apiserver)
27    location /websocket {
28        proxy_pass http://apiserver/websocket;
29        proxy_http_version 1.1;
30        proxy_set_header Upgrade $http_upgrade;
31        proxy_set_header Connection $connection_upgrade;
32        proxy_set_header Host $http_host;
33        proxy_set_header X-Real-IP $remote_addr;
34        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
35        proxy_read_timeout 86400;
36    }
37
38    # proxy http requests to moonraker (apiserver)
39    location ~ ^/(printer|api|access|machine|server)/ {
40        proxy_pass http://apiserver$request_uri;
41        proxy_http_version 1.1;
42        proxy_set_header Upgrade $http_upgrade;
43        proxy_set_header Host $http_host;
44        proxy_set_header X-Real-IP $remote_addr;
45        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
46        proxy_set_header X-Scheme $scheme;
47    }
48
49    # proxy http request to ustreamer (ustreamer)
50    location /webcam/ {
51        postpone_output 0;
52        proxy_buffering off;
53        proxy_ignore_headers X-Accel-Buffering;
54        access_log off;
55        error_log off;
56        proxy_pass http://ustreamer1/;
57    }
58  }
59
60  # referenced by the location /websocket (moonraker running on 7125)
61  upstream apiserver {
62    ip_hash;
63    server 127.0.0.1:7125;
64  }
65
66  # referneced by location /webcam/ (ustreamer running on port 8080)
67  upstream ustreamer1 {
68    ip_hash;
69    server 127.0.0.1:8080;
70  }
71}

Find a full nginx.conf for such reverse proxy setup in MainsailOS.

Let’s breakdown and explain some of the sections.

Lines 3-6, this condition nginx to send a Connection: Upgrade header when a Upgrade header is present. This enables Moonraker and its clients to switch from HTTP to Websocket.

Line 9, reverse proxy server will listen on port 80.

Line 13, nginx will look for Mainsail assets in this path and serve them over port 80.

Lines 27-36, proxies the request to :80/websocket from clients. Make sure appropriate HTTP headers are passed, e.g. Connection: Upgrade to upgrade protocols, X-Real-IP for Moonraker’s truste_cllients checks.

Lines 39-47, proxies other moonraker requests. These are typically HTTP and not Websocket.

Lines 50-57, proxies :80/webcam/ to :8080/ (ustreamer). The various settings prevents stream delays by disabling buffering.

Lines 61-64, defines upstream apiserver (moonraker) for referencing in this config, e.g. line 28, 40.

Lines 67-70, defines upstream ustreamer (ustreamer) for referencing in this config, e.g. line 56.

You can proxy more webcams by duplicating and renaming the location /webcam/ (line 50-57) and upstream ustreamer (lines 67-70) sections.

Now you should have a high level understanding of how all these services connect and interact with each other. You should also be able to configure a simple reverse proxy using nginx.