prind
prind copied to clipboard
Setting up project in portainer
I am currently using the klipper-web-control-docker project for controlling my printer from my nas, but I discovered this project and I am interested in trying it out.
Is there documentation on how to set it up in Portainer? My biggest issue is having to populate the docker-compose.override.yaml, which I don't think it's possible in Portainer
There is currently no documentation on how to set this up in portainer, as I am unfamiliar with it.
From a short glance at the portainer docs, there is no way to provide an override file for compose, I'm also unsure about if portainer actually uses compose under the hood or is just able to interpret the compose spec.
You could try to add your personal settings directly to the main docker-compose file and then uploading it to portainer.
The issue I see here is, that the config directory is used as a docker volume and I'm not sure if you can upload the directory structure with the prefilled config files into portainer.
You may want to create a simple docker-compose file that only consists of the services you'd like to run and also holds the config files in an actual docker volume instead of a bind mounted directory from within this repository.
I'd be happy to add a portainer guide if you get it to work.
-Markus
@HitLuca Hello I'm by no means an expert on docker or Portainer.
With some help from a friend and a lot of reading online I'm able to run prind from Portainer (Community Edition 2.15.1) with the following configuration.
I have commend out the stuff i don't need and i can add what i want for example ustreamer inside the main config. Obviously you have to change folder paths etc to your need's and don't forget to change the "devices:" port under "klipper:".
I hope it helps.

Thanks @gminadak,
your config seems to be in line with my suggestion.
- It removes the profile functionality and sets up services in a static way
- it is using
/data/prindas a directory for all volume mounts that are usually created as docker volumes
I have two questions about this setup:
-
The comment you added at the Top of the file suggests to set permissions on that directory as traefik would then not find the moonraker container. Could you elaborate on which permissions should be set?
This sounds like what theinitService is doing once before moonraker/klipper start. Traefik probably has no issues in finding the moonraker container, but the container is stuck in a restart loop as the permissions on the files are set wrong. -
Is there a reason to not use docker volumes for gcode, run, log and moonraker-db and instead mount a local directory? I'd assume it would be enough to change the location for the config directory and keep using docker volumes for everything else.
-Markus
Based on the file provided by @gminadak and some of my own assumptions I created a file that might be used with portainer.
It currently lives in a separate branch but I'd like to merge it into main if I receive positive Feedback.
I'd be happy if one of you could give it a try and report back. Please pay attention to the setup notes at the Top of the file. https://github.com/mkuf/prind/blob/custom-templates/custom/docker-compose.custom.portainer.yaml
-Markus
Testing this approach now, as first feedback I can say that the yaml provided by @mkuf has a
profiles:
- fluidd
section that should be removed, or the fluidd container wouldn't launch. I had to remove the init container as I couldn't get it to work, but I seem to be able to use the printer without issues.
I am quite in a hurry these days so I cannot finish testing everything, but will add updates as I make progress
@mkuf Sorry for the late reply.
Having the volumes gcode, log and moonraker-db (run is on the default location) on different locations is my preference as portainer will place it under /var/lib/docker/volumes/

And because you can delete the volumes directly from Portainer you can very easily lose all you gcode files, and the moonraker-db (yeap I did that once!) also you need to sudo su every time you need to access this folder.
Make sure your printer is connected and powered on before you start the "stack" or Portainer will throw an error (the first time only then it will work fine as long as the stack runs)

and you will have to remove the containers by hand for the stack to start again

Don't ever select this!!! If you use non-persistent volumes it will erase all data as I say before

I have already test your config as you can see and work's fine.
@mkuf Sorry my bad you need to remove lines 84, 85 from your config for fluid to start
Thanks for your Feedback @HitLuca and @gminadak
The profile has been removed from the fluidd service in https://github.com/mkuf/prind/commit/4fce13be42703abf74cecb66c6d34de0e4daeb42
Did the init conainer work for you @gminadak?
-Markus
@mkuf
- The comment you added at the Top of the file suggests to set permissions on that directory as traefik would then not find the moonraker container. Could you elaborate on which permissions should be set? This sounds like what the
initService is doing once before moonraker/klipper start. Traefik probably has no issues in finding the moonraker container, but the container is stuck in a restart loop as the permissions on the files are set wrong.
As I test the configuration and starting to remember thinks....the second reason is that Portainer will make all folders on the persistent volume as root as you mentioned
Strange think is it's the same as with non persistent volumes and works fine
and Traefik will start to throw errors.
As soon as I change the permissions to user everything work's fine

Did the init conainer work for you @gminadak?
From what I can see it changed the user:group on /config fine
but I had to create/copy the folder config by hand for the container to start

I had to restore my old VM-docker-portainer backup to test all this as I have now move the setup to VM without docker-portainer just plain KIAUH installation!
Thanks. Based on your feedback I'll merge the custom templates into main for everyone to use.
Implemented with https://github.com/mkuf/prind/commit/ff4ca0222645677ef88b765666e7a54d67d53fc1.
Feel free to create pull requests if you encounter issues with the portainer template.
@mkuf I recently assembled a new 3d printer, and had to do a multi-printer setup in portainer. It may be helpful to know what a working code would look like, I took out traefik and basically duplicated the examples in order to get it to work
I have both mainsail and fluidd running, and each printer has been connected using the my_hostname:8013 and my_hostname:8014. I don't have webcams setup yet so I took out the streamer service. Should be easy enough to add back tho.
services:
eryopne-thinker-se-klipper:
image: mkuf/klipper:latest
restart: unless-stopped
logging:
driver: none
depends_on:
eryopne-thinker-se-init:
condition: service_completed_successfully
command: -I printer_data/run/klipper.tty -a printer_data/run/klipper.sock printer_data/config/printer.cfg -l printer_data/logs/klippy.log
devices:
- ${ERYONE_THINKER_SE_SERIAL_INTERFACE}:${ERYONE_THINKER_SE_SERIAL_INTERFACE}
volumes:
- ${ERYONE_THINKER_SE_CONFIG_FOLDER}/config:/opt/printer_data/config
- ${ERYONE_THINKER_SE_CONFIG_FOLDER}/run:/opt/printer_data/run
- ${ERYONE_THINKER_SE_CONFIG_FOLDER}/gcode:/opt/printer_data/gcodes
- ${ERYONE_THINKER_SE_CONFIG_FOLDER}/log:/opt/printer_data/logs
labels:
org.prind.service: klipper
org.prind.printer: eryopne-thinker-se
eryopne-thinker-se-moonraker:
image: mkuf/moonraker:latest
restart: unless-stopped
pid: host
privileged: true
#logging:
# driver: none
depends_on:
eryopne-thinker-se-init:
condition: service_completed_successfully
eryopne-thinker-se-klipper:
condition: service_started
ports:
- 8013:7125
volumes:
- /dev/null:/opt/klipper/config/null
- /dev/null:/opt/klipper/docs/null
- /run/dbus:/run/dbus
- /run/systemd:/run/systemd
- ${ERYONE_THINKER_SE_CONFIG_FOLDER}/run:/opt/printer_data/run
- ${ERYONE_THINKER_SE_CONFIG_FOLDER}/gcode:/opt/printer_data/gcodes
- ${ERYONE_THINKER_SE_CONFIG_FOLDER}/log:/opt/printer_data/logs
- ${ERYONE_THINKER_SE_CONFIG_FOLDER}/moonraker-db:/opt/printer_data/database
- ${ERYONE_THINKER_SE_CONFIG_FOLDER}/config:/opt/printer_data/config
labels:
org.prind.service: moonraker
org.prind.printer: eryopne-thinker-se
eryopne-thinker-se-init:
image: busybox:latest
command: chown -R 1000:1000 /prind/config
volumes:
- ${ERYONE_THINKER_SE_CONFIG_FOLDER}:/prind
labels:
org.prind.service: init
org.prind.printer: eryopne-thinker-se
voron-0-klipper:
image: mkuf/klipper:latest
restart: unless-stopped
logging:
driver: none
depends_on:
voron-0-init:
condition: service_completed_successfully
command: -I printer_data/run/klipper.tty -a printer_data/run/klipper.sock printer_data/config/printer.cfg -l printer_data/logs/klippy.log
devices:
- ${VORON_0_SERIAL_INTERFACE}:${VORON_0_SERIAL_INTERFACE}
volumes:
- ${VORON_0_CONFIG_FOLDER}/config:/opt/printer_data/config
- ${VORON_0_CONFIG_FOLDER}/run:/opt/printer_data/run
- ${VORON_0_CONFIG_FOLDER}/gcode:/opt/printer_data/gcodes
- ${VORON_0_CONFIG_FOLDER}/log:/opt/printer_data/logs
labels:
org.prind.service: klipper
org.prind.printer: voron-0
voron-0-moonraker:
image: mkuf/moonraker:latest
restart: unless-stopped
pid: host
privileged: true
#logging:
# driver: none
depends_on:
voron-0-init:
condition: service_completed_successfully
voron-0-klipper:
condition: service_started
ports:
- 8014:7125
volumes:
- /dev/null:/opt/klipper/config/null
- /dev/null:/opt/klipper/docs/null
- /run/dbus:/run/dbus
- /run/systemd:/run/systemd
- ${VORON_0_CONFIG_FOLDER}/run:/opt/printer_data/run
- ${VORON_0_CONFIG_FOLDER}/gcode:/opt/printer_data/gcodes
- ${VORON_0_CONFIG_FOLDER}/log:/opt/printer_data/logs
- ${VORON_0_CONFIG_FOLDER}/moonraker-db:/opt/printer_data/database
- ${VORON_0_CONFIG_FOLDER}/config:/opt/printer_data/config
labels:
org.prind.service: moonraker
org.prind.printer: voron-0
voron-0-init:
image: busybox:latest
command: chown -R 1000:1000 /prind/config
volumes:
- ${VORON_0_CONFIG_FOLDER}:/prind
labels:
org.prind.service: init
org.prind.printer: voron-0
mainsail:
image: ghcr.io/mainsail-crew/mainsail:edge
restart: unless-stopped
ports:
- 8011:80
volumes:
- ${MAINSAIL_CONFIG_FOLDER}/mainsail-config.json:/usr/share/nginx/html/config.json
labels:
org.prind.service: mainsail
fluidd:
image: cadriel/fluidd:latest
restart: unless-stopped
ports:
- 8012:80
labels:
org.prind.service: fluidd
The setup uses portainer env vars to work, here is what I used:
MAINSAIL_CONFIG_FOLDER=/.../prind
ERYONE_THINKER_SE_CONFIG_FOLDER=/...prind/eryone_thinker_se
ERYONE_THINKER_SE_SERIAL_INTERFACE=/dev/serial/by-id/...
VORON_0_CONFIG_FOLDER=/.../prind/voron_0
VORON_0_SERIAL_INTERFACE=/dev/serial/by-id/...
RPI_PICO_SERIAL_INTERFACE=/dev/serial/by-id/...
the RPI_PICO_SERIAL_INTERFACE is used whenever I want to do input shaping, I would just have to add a new device mapping to the relevant klipper service and restart it.
As you can see I use separate folders for each printer, in my opinion this is nicer since each printer is completely separate from the other.
The mainsail-config.json file just contains
{
"instancesDB": "json",
"instances": [
{ "hostname": "my-hostname", "port": 8013 },
{ "hostname": "my-hostname", "port": 8014 }
]
}
to have the printer already set up in the web interface
To avoid issues with moonraker blocking mainsail/fluidd, and since it's a local-only setup, I removed authorization and allowed all local ips to connect to the services like this (moonraker.conf)
[machine]
provider: systemd_cli
validate_service: False
[authorization]
force_logins: False
trusted_clients:
10.0.0.0/8
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.168.0.0/16
FE80::/10
::1/128
cors_domains:
*
[octoprint_compat]
[history]
The single issue I'm having is that fluidd complains that I don't have a CANCEL_PRINT macro, even though I have it on my configs (using the https://github.com/jschuh/klipper-macros project which has been working just fine since I started using it)
The label org.prind.printer has been used in order to restart klipper whenever a printer is power cycled, following my solution in https://github.com/mkuf/prind/issues/51, and just changing how you get the correct klipper container in the script you posted (klipper_container=$(docker ps -aqf "label=org.prind.printer=the_interested_printer")). I have a restart_printer.sh script for each printer in their prind subfolder,and a udev rule which checks for both mainboards for power cycling. When this happens it just calls the correct script
May as well also add my configuration for klipperscreen:
In portainer
klipperscreen:
image: mkuf/klipperscreen:latest
restart: unless-stopped
network_mode: host
environment:
DISPLAY: ${KLIPPERSCREEN_IP}
volumes:
- /etc/localtime:/etc/localtime:ro
- /tmp/.X11-unix:/tmp/.X11-unix
- ${CONFIG_FOLDER}/klipperscreen.conf:/opt/cfg/klipperscreen.conf
labels:
org.prind.service: klipperscreen
the env var KLIPPERSCREEN_IP is the ip of my phone as shown in the xserver app, like 192.168.1.2:0
I moved the klipperscreen.conf file in the root of my prind config folder, and the contents for two printers are
[printer eryone-thinker-se]
moonraker_port: 8013
[printer voron-0]
moonraker_port: 8014
#~# --- Do not edit below this line. This section is auto generated --- #~#
#~#
#~# [main]
#~# use_dpms = False
#~#