As I’ve built up my smart home, I’ve found myself using an increasing number of interdependent services. As they can cause issues with each other, I was very attracted to Docker as a way to keep everything isolated and portable. That said, I ran into some hiccups with my setup, so I thought I’d share a few of my suggestions and best practices in the hopes they help someone out there.
I am far from an expert, and these are my experiences. I’ve learned by failing and by lots of googling and trial and error. They are not the only, or even necessarily the best way to do things, just the best as far as I’ve learned. I’m very open to suggestions, though my primary intent with this post is to help others get through some of the stickier points.
Also, my only experience with Docker has been running on an x86 Ubuntu server. While much of this probably will apply to other systems (especially Macs and other Linux flavors), I can’t guarantee that.
I use Docker to run the following services on my server.
- openHAB Home Automation
- MotionEye CCTV
- Emby media server
- Logitech Media Server + 2 SqueezeLite instances for audio streaming
- ShairPort Sync for audio streaming from Apple devices
- InfluxDB
- Grafana for beautiful graphs
- Frontail for viewing logs
- UniFi poller for bringing UniFi data into my Grafana graphs
- Portainer for container management
My list of containers in Portainer
That’s a lot of software with conflicting requirements, and I think it’d be really hard to keep it all running happily using just SystemD. This is where Docker really shines!
I strongly recommend Portainer to keep track of your Docker containers. While you can theoretically do everything you need from the command line, I find it very helpful to view everything at a glance and make small changes. I would make getting that set up the first step of a successful Docker setup.
Next, I’d identify software that doesn’t necessarily benefit from the Docker experience. Initially, I planned a strict Docker-only stance for my IoT services, but I found that to be less than optimal. nodeRED tended (for me) to work better installed directly on the host machine, and I also (before I switched to MotionEye) found that Shinobi CCTV worked better directly on the machine. I don’t remember why, but I had trouble with my Mosquitto container and decided just to run it as a SystemD service. Finally, I used QLC+ for DMX control, and that seems to work best on its own system, so it got its own RPi3. YMMV.
Next, I start with the service’s entry on Docker Hub and look at the docker run
examples. I take the example, create a text file on my desktop computer, and use that as a template. That way, if I ever need to recreate the container, I can do so using the exact same variables as I used to build it. An important caveat here is that, if you make changes to your container using Portainer, that it’s important to include these changes in your notes.
Next, if my container requires a persisted volume (as most do), I always use a bound volume using the I prefer the ability to have all of my container data in one place. I use the /opt
directory, and create a subdirectory for the service, i.e., /opt/grafana
. I prefer the ability to have all of my container data in one place. I use the /opt
directory, and create a subdirectory for the service, i.e., /opt/grafana
. I make sure the run
command reflects the proper file path:
-v /opt/grafana/data:/var/lib/grafana \
Note that for Docker flags the host machine’s info is on the left and the container is on the right. For this example, the folder /opt/grafana/data
appears to the container as /var/lib/grafana
.
Next, and I think this issue creates some of the biggest headaches: I consider file permissions. If the container doesn’t have permission to view the files inside it’s directory, it can’t function properly. To avoid going in to unnecessary detail I’m going to link some recommended reading here:
https://www.tecmint.com/add-users-in-linux/
Basically, to get your permissions straight, you will want to do the following:
- Create a new user and group for your service (i.e.,
grafana
) with a specific user and group ID (I used 9004 for grafana) - Add your primary user to the group, so you have read/write permissions over the files
- Give ownership to the new user:
sudo chown -Rv grafana:grafana /opt/grafana
- Change permissions:
sudo chmod -Rv 775 /opt/grafana
- Make Docker run the container as the proper user (add to your Docker Run):
--user=9004 \
At this point, you should be ready to try firing up the container. I keep an instance of Portainer up so I can monitor the new container. The Log is often very helpful in diagnosing issues that keep containers from coming up or working properly.
Here’s my full Grafana run example (minus a few settings I set here that would be unnecessary for the purposes of demonstration):
sudo docker run \
-d \
-p 3000:3000 \
-p 8081:8081 \
--restart=always \
--name=grafana \
--user=9004 \
-e GF_SECURITY_ALLOW_EMBEDDING=true \
-e GF_AUTH_ANONYMOUS_ENABLED=true \
-v /opt/grafana/data:/var/lib/grafana \
grafana/grafana
My container is now happily running, and is set up to run on every reboot. If I need to make changes, I can test them in Portainer but make sure to keep them documented in my text file. This way, if I every need to start over with a fresh install of my server, I can do so with a copy of my /opt
folder and a collection of saved docker run
commands.
One final but useful piece of info: how to SSH into your container. For this example, I’ll use InfluxDB as it’s the only container that I needed to SSH in to for setup purposes. For a container named influxdb
use this command:
docker exec -it influxdb /bin/bash
You then can interact with the container directly. In the case of InfluxDB, this is the easiest way to create new users and databases.
I hope this is helpful! For many of you this is probably repeating things you know well, but I hope this might be helpful for someone!