Guides

3D Printer Farm with OctoPrint and Docker: Control Multiple Printers with a Single Raspberry Pi

OctoPrint is designed to be used with a single printer. But what do you do if you want to control multiple printers at the same time, and remotely manage your 3D printer farm? Using Docker containers could be an option, so in this guide I will show you how to set up multiple instances of OctoPrint with Docker.

Prerequisites for multiple OctoPrint instances

To control multiple 3D printers with OctoPrint and Docker, you need the following:

  1. Raspberry Pi (Raspberry Pi 4 with 4GB is strongly recommended for best performance with multiple Docker containers).
  2. Good quality 16 GB MicroSD card or higher.
  3. Good power supply for the Pi – Any adapter works if it’s capable of delivering at least 2.5 A of power
  4. Docker service already installed

How to set up OctoPrint in Docker?

Install Docker

Before we install multiple OctoPrint instances, we first need to install Docker on our Raspberry Pi. I previously covered this process in a previous article: Install Docker on Raspberry Pi. The installation process is easy, but it’s crucial to carefully follow every step of the guide for a successful installation. After Docker is installed and ready to go, you can move on the next step.

Create and edit the docker-compose file

I use Docker Compose to setup multiple OctoPrint instances in Docker. Docker Compose takes the settings we define in the file and instructs Docker how to create the new container.

First, we need to create a folder where to store all out Docker containers. Run the following command:

mkdir /home/dietpi/docker

Next, we need to copy the OctoPrint contaier definition in the docker-compose.yml file. Run the following command, to open and edit the file:

nano /home/dietpi/docker/docker-compose.yml

Copy the contents and paste them into the terminal window

version: '3.6'
 services: 
 Octoprint
 octoprint1:
      container_name: Octoprint1
      image: octoprint/octoprint
      hostname: Octoprint1
      restart: unless-stopped
      ports:
        - 5000:5000
        - 8080:8080
      volumes:
        - /home/dietpi/docker/octoprint1:/octoprint1
      environment:
        - ENABLE_MJPG_STREAMER=true
        - MJPG_STREAMER_INPUT=-n -d /dev/video0:/dev/video0 -r 1280x720 -f 30
      devices:
      #Printer Port
        - /dev/ttyACM0:/dev/ttyACM0
      #Webcam port
      #  - /dev/video0:/dev/video0

The file contents should look like this. Save the file by pressing CTRL+X then Y to confirm

Single Octoprint instance in Docker Compose

Make sure you paste the code correctly, because the text alignment is important for a successful deployment. If there are extra spaces and the settings are not correctly defined, you will get errors when trying to deploy the OctoPrint Docker container.

To start the OctoPrint Docker container, run the following command:

docker-compose -f /home/dietpi/docker/docker-compose.yml up -d

Docker will start pulling the latest version of OctoPrint, and install it. When completed, the docker container will be started and you will be able to access it via the IP and port defined. In my case, it’s https://192.168.0.197:5000.

image | 3D Printer Farm with OctoPrint and Docker: Control Multiple Printers with a Single Raspberry Pi

How to add multiple OctoPrint Docker instances?

Now that we have the first container up and running, we can start deploying extra OctoPrint containers. Just like we did in the previous deployment, we need to open the docker-compose.yml file just like we did before and add another container

In the example below, I added the OctoPrint 2 container underneath the previous one.

version: '3.6'
 services: 
 Octoprint
 octoprint1:
      container_name: Octoprint1
      image: octoprint/octoprint
      hostname: Octoprint1
      restart: unless-stopped
      ports:
        - 5000:5000
        - 8080:8080
      volumes:
        - /home/dietpi/docker/octoprint1:/octoprint1
      environment:
        - ENABLE_MJPG_STREAMER=true
        - MJPG_STREAMER_INPUT=-n -d /dev/video0:/dev/video0 -r 1280x720 -f 30
      devices:
      #Printer Port
        - /dev/ttyACM0:/dev/ttyACM0
      #Webcam port
      #  - /dev/video0:/dev/video0
  #####Octoprint2
   octoprint2:
      container_name: Octoprint2
      image: octoprint/octoprint
      hostname: Octoprint2
      restart: unless-stopped
      ports:
        - 5001:5000
        - 8081:8080
      volumes:
        - /home/dietpi/docker/octoprint2:/octoprint2
      environment:
        - ENABLE_MJPG_STREAMER=true 
        - MJPG_STREAMER_INPUT=-n -d /dev/video0:/dev/video1 -r 1280x720 -f 30
      devices:
      #Printer Port
        - /dev/ttyACM0:/dev/ttyACM1
      #Webcam port
      # - /dev/video0:/dev/video1

This is how the docker-compose.yml file should look like.

Docker Compose for Multiple Octoprint Instances | 3D Printer Farm with OctoPrint and Docker: Control Multiple Printers with a Single Raspberry Pi

As usual, run the docker-compose up command, to re-create the containers based on the modified file.

docker-compose -f /home/dietpi/docker/docker-compose.yml up -d

You can add as many OctoPrint Docker containers as you want, following the same format. Just remember to use different container names, and correctly map the devices, ports, and volumes.

Please note that for the second container, I haven’t added the version and services definition. This needs to be added only once, at the top of the file. Also note that I set the container name to Octoprint2, the volume location to /home/dietpi/docker/octoprint2 and the external port to 5001 for OctoPrint and 8081 for MJPG Streamer.

If you want to use a webcam, make sure you connect it before creating the container, and uncommenting the Webcam port definition. (delete the # before – /dev/video…).

Monitoring the resource consumption

The amount of OctoPrint Docker containers you can run is only limited by the resources of your Raspberry Pi, so it’s a good idea to keep an eye on the resource usage. If you want to have an easy way to monitor the resources used by the containers, I recommend installing Netdata.

Netdata on Raspberry Pi | 3D Printer Farm with OctoPrint and Docker: Control Multiple Printers with a Single Raspberry Pi

The installation is easy. Just paste a single line in the terminal, then press enter to start the install process.

bash <(curl -Ss https://my-netdata.io/kickstart.sh)

After a few minutes, the lastest version of Netdata will be downloaded and installed on your Raspberry Pi. It’s running on port 19999.

Keep in mind that Netdata will take extra resources on your Raspberry Pi so I recommend using it only on Raspberry Pi 4. I use it on an Intel NUC which hots all my 3D printing docker containers.

Wrapping up

I hope this article was helpful, and you started to develop a taste for Docker. As you can see, Docker simplifies the task of adding multiple services to a single Raspberry Pi. If you encounter any issues with the installation, feel free to join the 3DPrintBeginenr Discord Server or leave a comment below. In a future article, I will show you how to add a VPN server to Docker, which will ensure a secure connection to OctoPrint from anywhere in the world.

Liked it?
Consider supporting 3DPrintBeginner if this content helped. You can also join Patreon for exclusive perks!

Related Articles

You can leave a comment for this article on the 3DPrintBeginner Forum

23 Comments

  1. Thank you for this informative guide! I’m currently trying this out in a VM on my Synology to see if I can use it rather than using my (very valuable) RPi 4 to run two printers.

    One thing of note, which I finally figured out, is that your text to copy above, has incorrect indentation and does not match the screenshot below (the screenshot is what made me realize why I was getting a “yaml: line 1: did not find expected key” error when following the direction as provided so exactly!). After modifying the commenting and indentation to match the screenshot, it worked.

    Thanks again, as without this guide and direction, I would likely still be stumbling around the interwebs looking for solutions.

  2. Great guide thanks a ton. I was able to get both my prusa mk3s controlled by the same raspberry pi freeing up my other pi for my other 3 printers now that pis are expensive. It also solved a long running problem of having different files on both my prusas then printing an old version. I was able to add this line under the volumes.

    – /home/dietpi/docker/files:/octoprint/octoprint/uploads

    I can now have all the same files shared between both printers without having a network drive or doing anything crazy.

    Also thanks for turning me on to dietpi

  3. So I was having the issue with not getting my webcam stream to load in my containers. It seems to be a port binding issue.

    My working compose config contained
    “ports:
    – 80:80”

    But my other non-working compose configs contained
    “ports:
    – 3001:5000”

    OctoPrint uses both 80 and 5000. However, Octoprint uses /etc/haproxy/haproxy.cfg that binds the public address to port 80 but tries to bypass the public port if “/webcam/” is used. I think this is where the Webcam config is falling down if you do not bind to port 80 on the container. I suggest for those that are having issues just map port 80 in the container to whatever port you want on the host. E.g. 3001:80, 3002:80, 3003:80.

  4. sorry , I missed to specify that the webcam, in the first container is working well, but MJPG from the second one is not reachable. so results in a “website not reachable”. The 2nd container is up and running, but thats all.

    1. You need to check the different video sources on the Raspi’s system:
      v4l2-ctl –list-devices
      For example, on my system the webcam can be found as /dev/video0 and the Raspi camera under /dev/video2.
      However, when they then mount that into the various containers, they always have to set /dev/video0.
      So for container1 with the USB webcam, I have the following Docker Compose configuration:
      # webcam port
      – /dev/video0:/dev/video0

      And for container2 with the RaspiCam I have this configuration:
      # webcam port
      – /dev/video2:/dev/video0

      Integration of the stream see my answer at Derpenstein

  5. I’m not able to get the webcam running as it did using Octoprint as a standalone solution. I saved my configuration and imported it into the container during configuration / installation process. But no webcam. Its connected first and then the container is created and it can be found at /dev/video0. Any suggestions ?

  6. Hey guys some followup here would be extremely beneficial. Concerning the threads between Wade and 3DPB: can I get clarification as to how the actual strings should look? octoprint1/2 vs octoprint/octoprint or octoprint/octoprint1/2 or whatever and however it’s supposed to be. I receive error “mapping not allowed here” whenever I try to execute the container command, so obviously I’m messed up somewhere. Thanks!!!

  7. Here is how you can assign a static file descriptor to each USB device. That away when you turn on your printers in a different order than the previous time, the USB file descriptors will remain the same, no matter the order the printers are turned on.

    https://www.freva.com/2019/06/20/assign-fixed-usb-port-names-to-your-raspberry-pi/

    The lines of my docker compose file now look like this:

    – /dev/ttyUSB_WANHAOI3:/dev/ttyACM0
    – /dev/ttyUSB_AQUILA:/dev/ttyACM0

    1. As an alternative it is possible to identify the device by its Id e.g.

      ‘/dev/serial/by-id/usb-marlinfw.org_Marlin_….’

      You can find this ‘by-id’ listed as one of the properties when using

      dmesg | grep tty
      udevadm info –name=/dev/ttyACM0

      With this approach you don’t need the extra udev rule and can bind a specific printer to a specific port. I haven’t tried using multiple Marlin based printers, but I assume that they will have different identifiers.

      1. This alternative (going by ID) has worked best for me. One issue however, utilizing this method with an Ender 3 (V2) and a Prusa MK3S+, the Ender container will spit the following error and will not start if that printer isn’t powered on:

        Error response from daemon: error gathering device information while adding custom device “/dev/serial/by-id/usb-xxxx_USB_Serial-xxxx-port0”: no such file or directory

        Which I can understand. However, curiously, the Prusa container was built in the same way and will not spit that error if the printer is powered off. I would eventually like to add power relay switching functionality to Octoprint via plugin for both printers. I noticed through this process that the printer USB drivers load differently, the Ender loads through ttyUSB and the Prusa loads through ttyACM. Perhaps this is related.

        Any ideas on how to get docker to ignore the error and startup? or am I SOL and just need to make sure the printers are up whenever the rpi restarts?

  8. Pretty sure the following two lines are wrong:

    – /home/dietpi/docker/octoprint1:/octoprint1
    – /home/dietpi/docker/octoprint2:/octoprint2

    The internal container mount should be “octoprint”, not “octoprint1/2”. Octoprint is writing it’s config, etc to /octoprint. If you do 1/2 on the end then the Octoprint settings are not retained when you stop the docker containers with “docker-compose.yml down”.

    1. Also pretty sure these two lines are wrong:

      – /dev/ttyACM0:/dev/ttyACM0
      – /dev/ttyACM0:/dev/ttyACM1

      The host pointers are on the left hand side of the colon. Guest pointers on right hand side. So left had side should say ACM0 and ACM1 while right had side should be ACM0 for both containers. Here’s how mine looks running under Raspbian Lite:

      – /dev/ttyUSB0:/dev/ttyACM0
      – /dev/ttyUSB1:/dev/ttyACM0

      1. I think you missed my point. In the section of your tutorial you can cut and paste from, you do not have # in front of the third line that says “Octoprint”, while in the screen shots you do. Also, your spacing is a bit off in the text you can cut and paste from.

Leave a Reply

Your email address will not be published. Required fields are marked *