Built one of our projects, and want to expand upon it? Perhaps you want to build a second one, but lack the hardware? Fear not! One of the great benefits of balenaCloud is that you can combine different projects and run everything on one device. In this short guide, we’re going to take a look at how to run multiple projects on a single device, allowing you to do more with the hardware you already have.
- The idea behind multiple projects on one device
- How to merge separate multi-container projects (in theory)
- Let’s merge balenaSound and Pi-hole
- Let’s merge balenaSound, Pi-hole and balenaSense
- Making it permanent (git submodules)
If you’ve already built one of our projects, be it balenaSound, Dash or Sense, or you already have your own project running on balenaCloud, awesome! This
Bud’s one’s for you, that’s the perfect start to this guide.
If you haven’t built a project yet, check out these guides first, as we’re gonna be building on top of those in order to show you how to complete more than one project with only one device.
I wanted to share this idea as it’s a question that comes up quite frequently on our support and in our forums. Whilst it isn’t immediately obvious how to do it, it’s totally possible and quite simple once you know how. The instructions here have been written in a generic manner with a followup example, with the aim that you can use them to combine any two projects, not only balena ones.
The idea behind multiple projects on one device
When running any project on any device, that software is unlikely to be using all the resources available. For example when running Pi-hole on a Raspberry Pi, we’re not using the audio output, so why not add balenaSound functionality on the same device? Similarly when running balenaSense, you’re not using the display output, so why not add balenaDash functionality to show your dashboard on a display connected to the same device.
The same goes for any project that you’ve developed, too. With balena, one device doesn’t have to mean one project; as long as your two (or more) applications aren’t fighting for the same hardware resources, there’s no reason why they cannot share the device they’re living on.
How to merge separate multi-container projects (in theory)
Each project, if it’s a multi-container project, contains a
docker-compose.yml file, and some subfolders, one for each service (container) that make up that project. The Docker compose file is essentially a list of all the services that make up that application, so it points to each of the subfolders. Very simply, what we have to do to combine projects is to merge the compose file from each project and add all the subfolders from one to the other.
There’s a bit more to it than this, but that’s the general idea.
Merging two multi-container applications
If the project you’re merging is made up of only one service, it will have a
Dockerfile in the root instead of a
docker-compose.yml, and that means we can add that project as a subfolder within our main project.
Merging a single container application with a multi-container application
Possible conflicts along the way
Probably the most common conflict you’ll come across between two projects is that of networking. Say for example you are merging two projects that each have their own web server: both could be listening for requests on port 80. It’s not possible for multiple services to listen on the same network port, so one will have to be moved. The services can listen on the same port internally, but they must be mapped to different ports externally. There are ways around this by using software such as Traefik, but that’s for another post.
Altering duplicated ports to enable both services to be accessible externally
Let’s merge balenaSound and Pi-hole
OK, enough talk! Let’s hit this. To work through this, I’m assuming you’ve got a terminal available with
git installed along with
balena-cli. If you’ve already successfully deployed one of these projects in the past, you should be ready to go. Initially we’re going to simply copy and paste services to merge them (no git installation required), but later in the guide we’re going to look at doing it with Git submodules for a more maintainable solution.
Step 1: Let’s create a new directory for our merged project, I’m calling mine
balena-sound-hole. In the CLI, I use this command (and other commands in their respective steps):
Step 2: Next, download the Pi-hole project and the balenaSound project to a new folder on your computer, using either
git clone or by downloading the
.zip file from each respective repo, in which case you can skip the
git clone commands below, and instead extract the downloaded zip files into the new folder.
git clone firstname.lastname@example.org:klutchell/balena-pihole.git git clone email@example.com:balena-io-projects/balena-sound.git
I now have three directories in this folder:
Step 3: I’m going to copy the services from each of the two apps into my new
cp balena-pihole/docker-compose.yml balena-sound-hole/ cp -R balena-pihole/pihole balena-sound-hole/pihole cp -R balena-pihole/dnscrypt-proxy balena-sound-hole/dnscrypt-proxy cp balena-sound/docker-compose.yml balena-sound-hole/docker-compose-sound.yml cp -R balena-sound/core balena-sound-hole cp -R balena-sound/plugins balena-sound-hole
Step 4: Let’s merge the two
docker-compose.yml files. I copied the file from each project into our new project, renaming the
balena-sound one to
docker-compose-sound.yml in the process. Open both files from the
balena-sound-hole directory side by side in your editor of choice.
There are two things for us to do here. First, we copy all the service definitions from
docker-compose.yml. It’s important to note how these blocks are indented and keep this formatting. You should end up with a
services: block, that has the following blocks underneath it:
Note that we are merging the
services blocks from each file, so there should only be one
Now, as both of these projects have volumes (data storage areas) defined in their respective
docker-compose.yml files, we need to merge these as well. The resultant file should only have a single
volumes: block, under which it should have
Step 5: It’s time for the big moment: you can now run
balena push <appName> from this directory in the same way you did when you deployed your first application. Replace
<appName> with the name of your balenaCloud application.
A successful outcome
If the project built successfully, you should now find that your device is running the entire balenaSound app alongside the Pi-hole app, greatly adding to what your single device is able to do!
EGADS! A multifunction device!
Let’s merge balenaSound, Pi-hole and balenaSense
OK, so that worked and seemed almost too easy! Let’s push our luck and add balenaSense to the app we just created, running 3 different applications on one device. Let’s call this new app
balena-sense-sound-hole? Maybe not.
Step 1: Working from the same directory you cloned balenaSound and Pi-hole into previously, let’s clone balenaSense, too.
git clone firstname.lastname@example.org:balena-io-projects/balena-sense.git
Step 2: Working through the same process as before, we’re going to copy all the services from balenaSense to our new app.
cp -R balena-sense/grafana balena-sound-hole/grafana cp -R balena-sense/influxdb balena-sound-hole/influxdb cp -R balena-sense/sensor balena-sound-hole/sensor cp -R balena-sense/telegraf balena-sound-hole/telegraf cp balena-sense/docker-compose.yml balena-sound-hole/docker-compose-sense.yml
Step 3: As before, we now take the services from the balenaSense compose file and add them to the main one. This time though, note that the service
grafana is going to want to use port 80. This will conflict with Pi-hole, which also uses port 80, so let’s change that to something else using the method mentioned earlier in the guide to make it available on port 3000.
Also as before, there is a data volume used by balenaSense called
sense-data. Copy this to the existing volumes block and add it below the rest.
Step 4: Now, push the application again! You should find that you are now building containers for all three applications!
Another successful outcome
Good lord! That's a lot of containers.
That turned out to be really straightforward, no special consideration required other than changing the port for the balenaSense Grafana instance to prevent it clashing with Pi-hole. I now have one Raspberry Pi 3 device running all three balena applications.
Making it permanent (git submodules)
Now, we merged these projects manually by copy and pasting the components into a new project, but this isn’t necessarily the best way of doing things. What if there are updates to the individual projects and you want to update your app to include the latest changes? We can do this by creating a new Git repository and pulling in the other projects as submodules.
The process is very similar, in that we still have to pull in all the separate projects, and then create a new
docker-compose.yml file for the root of the project. A Git installation is required for this part of the guide.
Step 1: First, create a new directory for your project and
cd into it. I called mine
mkdir balena-mashup cd balena-mashup
Step 2: Initialize this directory as a Git repository.
Step 3: Add the three projects as submodules.
git submodule add email@example.com:balena-io-projects/balena-sound.git git submodule add firstname.lastname@example.org:balena-io-projects/balena-sense.git git submodule add email@example.com:klutchell/balena-pihole.git
Step 4: Create the
docker-compose.yml file in the root of your new project directory (`balena-mashup/docker-compose.yml’) in the same way as described before. You’ll need to copy the services and volumes from each of the 3 projects just as before, but with a small difference; the build paths need to be prefixed with their folder name, as they’re now being run as a submodule.
For example, the
audio service by default says
build: ./core/audio but this needs to be updated to a path relative to our new
docker-compose.yml file, so in this case it would be
build: ./balena-sound/core/audio. Apply the same premise to all the services
Step 5: Push the project in the usual way:
balena push <appName>.
You’ve created your own Git repo with your project setup here, so you can go ahead and
git commit the changes in the usual way.
Step 6: Now, when the time comes, you can run
git submodule foreach git pull origin master and Git will update each of the individual projects with all the latest changes - neat, huh? After they’ve updated, just commit and push again to update your devices.
Hopefully this guide has served to demonstrate how simple it can be to use one device for multiple purposes on balena. There are of course a whole load of different combinations so we can’t possibly cover every single one, but hopefully with these examples you now have the knowledge to go ahead and try it for yourself.
Thanks for reading! If you decided to test this theory by merging some projects of your own, we’d love to hear how it went. Similarly, if you get stuck combining projects we haven’t covered here, or have any questions, let us know in our forums at https://forums.balena.io, on Twitter @balena_io, on Instagram @balena_io or on Facebook.