Post

Klipper from Scratch

I bricked and had to rebuild my 3D printer controller (a raspberry pi clone) and figured I would document the entire journey.

This is starting from a minimal install of Armbian (Debian based OS on a Raspberry Pi clone) that gives us a good foundation point to begin from (see my earlier post 2026-01-06 Armbian Trixie on how I got that ready).

Let’s start by setting some goals

  1. We want Klipper to run automatically
  2. We want it be isolated as much as possible
  3. We want minimal dependency
  4. Use Mainsail as Web-UI (requires Moonraker API layer)

We will use the Manual instructions from Mainsail to guide us, with a few small modifications to suit our environment

Setup a User account for services

We will run everything in its own account and we’ll create a suitable home folder for it all

1
sudo useradd -G tty,dialout -m -d /opt/klipper klipper

We need tty and dialout if we want serial access

We will also need a few utilities that may not be installed in a minimal configuration

1
sudo apt-get install zip unzip gzip

Install dependencies

1
sudo apt-get install virtualenv python3-dev python3-venv git build-essential libffi-dev libncurses-dev libusb-dev avrdude gcc-avr binutils-avr avr-libc stm32flash libnewlib-arm-none-eabi gcc-arm-none-eabi binutils-arm-none-eabi libusb-1.0

Klipper

Configure python virtual env

Now we have our base sytem ready, we can switch to the Klipper user and download sources and make the configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sudo su klipper -
cd ~
# Configure directories for Mainsail
mkdir ~/printer_data/
mkdir ~/printer_data/config
mkdir ~/printer_data/logs
mkdir ~/printer_data/gcodes
mkdir ~/printer_data/systemd
mkdir ~/printer_data/comms
touch ~/printer_data/config/printer.cfg
#Configure venv
python3 -m venv klippy-venv
source klippy-venv/bin/activate
pip install pip --upgrade
deactivate
exit

Install Klipper

Now we can install Klipper

1
2
3
4
5
6
7
sudo su klipper -
cd ~
git clone https://github.com/KevinOConnor/klipper
source klippy-venv/bin/activate
pip install -r klipper/scripts/klippy-requirements.txt
deactivate
exit

Configure Environment

We will need the environment to be defined for our systemd service

1
vi ~/printer_data/systemd/klipper.env
1
2
#/opt/klipper/printer_data/systemd/klipper.env
KLIPPER_ARGS="$/opt/klipper/klipper/klippy/klippy.py /opt/klipper/printer_data/config/printer.cfg -l /opt/klipper/printer_data/logs/klippy.log -I /opt/klipper/printer_data/comms/klippy.serial -a /opt/klipper/printer_data/comms/klippy.sock"

Configure Startup script

1
sudo vi /etc/systemd/system/klipper.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#/etc/systemd/system/klipper.service
#Systemd service file for klipper
[Unit]
Description=Starts klipper on startup
Documentation=https://www.klipper3d.org/
After=network-online.target
Wants=udev.target

[Install]
WantedBy=multi-user.target

[Service]
Type=simple
User=klipper
RemainAfterExit=yes
EnvironmentFile=/opt/klipper/printer_data/systemd/klipper.env
WorkingDirectory=/opt/klipper/klipper
ExecStart=$KLIPPER_HOME/klippy-venv/bin/python $KLIPPER_ARGS
Restart=always
RestartSec=10
1
2
3
sudo systemctl daemon-reload
sudo systemctl enable klipper.service
sudo systemctl restart klipper

Moonraker

Install Moonraker

1
2
3
4
5
6
7
sudo su klipper -
cd ~
git clone https://github.com/Arksine/moonraker.git
python3 -m venv ./moonraker-env
source moonraker-env/bin/activate
pip install -r ./moonraker/scripts/moonraker-requirements.txt
deactivate

Setup moonraker config

1
vi ~/printer_data/config/moonraker.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#/opt/klipper/printer_data/config/moonraker.conf
[server]
host: 0.0.0.0
port: 7125
# The maximum size allowed for a file upload (in MiB).  Default 1024 MiB
max_upload_size: 1024
# Path to klippy Unix Domain Socket
klippy_uds_address: ~/printer_data/comms/klippy.sock

[file_manager]
# post processing for object cancel. Not recommended for low resource SBCs such as a Pi Zero. Default False
enable_object_processing: False

[authorization]
cors_domains:
    *://*.local.lan
trusted_clients:
    127.0.0.0/8
    192.168.0.0/16
    FE80::/10
    ::1/128

# enables partial support of Octoprint API
[octoprint_compat]

# enables moonraker to track and store print history.
[history]

# this enables moonraker announcements for mainsail
[announcements]
subscriptions:
    mainsail

# this enables moonraker's update manager
[update_manager]
refresh_interval: 168
enable_auto_refresh: True

[update_manager mainsail]
type: web
channel: stable
repo: mainsail-crew/mainsail
path: ~/mainsail

Configure Moonraker Environment

1
2
3
sudo su klipper -
cd ~
vi ~/printer_data/systemd/moonraker.env
1
2
#/opt/klipper/printer_data/systemd/moonraker.env
MOONRAKER_ARGS="/opt/klipper/moonraker/moonraker/moonraker.py -d /home/pi/printer_data"

Configure Moonraker Service

1
sudo vi /etc/systemd/system/moonraker.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#/etc/systemd/system/moonraker.service
#Systemd moonraker Service
[Unit]
Description=API Server for Klipper
Requires=network-online.target
After=network-online.target

[Install]
WantedBy=multi-user.target

[Service]
Type=simple
User=klipper
SupplementaryGroups=moonraker-admin
RemainAfterExit=yes
WorkingDirectory=/opt/klipper/moonraker
EnvironmentFile=/opt/klipper/printer_data/systemd/moonraker.env
ExecStart=/opt/klipper/moonraker-venv/bin/python $MOONRAKER_ARGS
Restart=always
RestartSec=10
1
2
sudo systemctl enable moonraker
sudo systemctl start moonraker

Mainsail

Start by setting up NGINX

Install Nginx

1
2
3
4
sudo apt-get install nginx
sudo touch /etc/nginx/sites-available/mainsail
sudo touch /etc/nginx/conf.d/upstreams.conf
sudo touch /etc/nginx/conf.d/common_vars.conf

Configure Upstreams

1
sudo vi /etc/nginx/conf.d/upstreams.conf
1
2
3
4
5
6
7
8
9
10
11
12
# /etc/nginx/conf.d/upstreams.conf

upstream apiserver {
    ip_hash;
    server 127.0.0.1:7125;
}

## Example mjpegstreamer upstreams:
#upstream mjpgstreamer1 {
#    ip_hash;
#    server 127.0.0.1:8080;
#}

Configure Common_Vars

1
sudo vi /etc/nginx/conf.d/common_vars.conf
1
2
3
4
5
#/etc/nginx/conf.d/common_vars.conf
map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

Configure Mainsail site-available

1
sudo vi /etc/nginx/sites-available/mainsail
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# /etc/nginx/sites-available/mainsail

server {
    listen 80 default_server;
    # uncomment the next line to activate IPv6
    # listen [::]:80 default_server;

    access_log /var/log/nginx/mainsail-access.log;
    error_log /var/log/nginx/mainsail-error.log;

    # disable this section on smaller hardware like a pi zero
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_proxied expired no-cache no-store private auth;
    gzip_comp_level 4;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/css text/xml text/javascript application/javascript application/x-javascript application/json application/xml;

    # web_path from mainsail static files
    root /opt/klipper/mainsail;

    index index.html;
    server_name _;

    # disable max upload size checks
    client_max_body_size 0;

    # disable proxy request buffering
    proxy_request_buffering off;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location = /index.html {
        add_header Cache-Control "no-store, no-cache, must-revalidate";
    }

    location /websocket {
        proxy_pass http://apiserver/websocket;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_read_timeout 86400;
    }

    location ~ ^/(printer|api|access|machine|server)/ {
        proxy_pass http://apiserver$request_uri;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Scheme $scheme;
    }

    ## Upstream Webcam
    #location /webcam/ {
    #    postpone_output 0;
    #    proxy_buffering off;
    #    proxy_ignore_headers X-Accel-Buffering;
    #    access_log off;
    #    error_log off;
    #    proxy_pass http://mjpgstreamer1/;
    #}
}

Setup the Mainsail folder in the Klipper directory

1
2
3
4
5
6
sudo su klipper -
mkdir ~/mainsail
cd ~/mainsail
wget -q -O mainsail.zip https://github.com/mainsail-crew/mainsail/releases/latest/download/mainsail.zip && unzip mainsail.zip && rm mainsail.zip
exit
sudo chown -R klipper:www-data /opt/klipper/mainsail

Enable the Mainsail site

1
2
3
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/mainsail /etc/nginx/sites-enabled
sudo systemctl restart nginx

With that, we should now be able to access our Mainsail webserver - test by accessing http://<sbc-ip> in your web browser

This post is licensed under CC BY 4.0 by the author.