SimpNas server with docker - home assistant, radicale , jellyfin & esphome (tutorial)
Hi all,

I've been meaning to write this tutorial since last year , now right out of the gate I will say I am far from an expert.

This is how I set up my NAS Server and docker containers , but I will gladly accept any critique of my setup and advice to better secure or improve the overall setup.

To start with I did try OpenMediaVault, I got everything how I wanted it, however I felt that OpenMediaVault was too bloated for my needs and a little complicated in some of its options. Whilst on the Armbian forum ,seeking advice for getting the fan to work properly for my server (yes I did try tuxd3v 's ATS fan configuration but this didn't work after using a later kernel to get my sata card to work  (more on that later)).

I discovered SimpNAS , I posted a thread on this forum for all who are interested . SimpNAS was exactly what I was looking for , simple, elegant and easy .

So to begin I setup OpenMediaVault and long story short I had issues with the pine64 supplied Sata card , I wont go in to detail here as there are many threads that describe this issue, here is some slight insight

So I bought  a marvell 88SE9230 Sata card , which so far has been absolutely amazing , rock solid no matter what I throw at it, but to use said card I had to use a newer kernel which as described in the thread resulted in me not being able to use ATS to control the fan.

Now down to business , I have a rockpro64 with NAS case , tall heatsink , case fan, a marvell 88SE9230 Sata card, 2 SSD drives and a very decent SD card , the software is Armbian , with SimpNAS and docker installed and 6 containers  - Nginx, Portainer, Homeassistant , Radicale caldav server , Jellyfin media server and EspHome for my home automation gadgets. home assistant and radicale are exposed to internet so they are accessible from outside my local network .

so here are my instructions :

  1. burn Armbian Buster server to SD Card using imager of choice I typically use Gnome disks , which is simple to use .
  2. after booting ssh to armbian with root and password 1234
  3. Change root password
  4. Do not add new user when logged in as root
  5. apt update && apt upgrade (reboot)
  6. add ssh keys to root home to ease login with out password (optional )
  7.  type Armbian-config  and then select options > System > Other > install latest kernel (5.8+) (server will reboot)
  8.  type echo 0 >  /sys/devices/platform/pwm-fan/hwmon/hwmon2/pwm1  this is to turn off the fan which is continously running on the newer kernel .
  9. edit /etc/rc.local file and add fan settings for startup, I chose these, which allow the fan to run continously but at a whisper quiet setting more info here

rmmod pwm-fan
echo 0 > /sys/class/pwm/pwmchip1/export
echo 110 > /sys/class/pwm/pwmchip1/pwm0/duty_cycle
echo 500 > /sys/class/pwm/pwmchip1/pwm0/period
echo 1 > /sys/class/pwm/pwmchip1/pwm0/enable

      10. add udev rule so drives will show up nano /etc/udev/rules.d/99-marvell.rules.

ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1b4b", ATTR{device}=="0x9230", RUN+="/bin/bash -c 'echo %k > /sys/bus/pci/drivers/ahci/bind'"

      11.  reboot then type lsblk  to see if drives are discovered

      12. now install SimpNAS

wget; bash

13. follow setup wizard on url shown in terminal at the end of install .

          14. login into SimpNAS admin panel using username administrator and password set during wizard

          15. in admin panel Add Volumes to add the drives in  SimpNAS

          16. add users in user section

          17. add shares to drives

          18. at this point you now have your NAS setup , you can upload all the files you need to  via samba

          19. now onto portainer run this to get portainer installed and running,  Docker is installed when SimpNAS is.

docker volume create portainer_data

docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce

         20. now pull the images for the other containers, you can do this via the portainer UI (hence why I installed it first or via command line ) I like to do this now as it can take some time to pull the images ,although they will be installed with docker-compose later, if you prefer doing it all at once and don't want to know how docker works .

docker pull homeassistant/home-assistant:stable tomsquest/docker-radicale esphome/esphome nginx:latest

        21. now to install docker compose which will help later with renewing  lets encrypt certificates via certbot . first we need a few packages

apt install python3 python3-pip python3-setuptools python3-dev libffi-dev

       22. then using the newly installed pip3 install  wheel and docker-compose

pip3 install wheel

pip3 install docker-compose lets create a docker-compose.yml file to start our containers and provide all the info they need to get running. When docker compose is run it will create all the necessary directories and files based on this docker-compose file .

version: '3.7'
      - radicale
      - homeassistant
    image: nginx:latest
    container_name: nginx_reverse_proxy
      - /etc/localtime:/etc/localtime:ro
      - /root/nginx.conf:/etc/nginx/nginx.conf
      - /root/nginx/error.log:/etc/nginx/error.log
      - /root/nginx/access.log:/etc/nginx/access.log
      - /etc/letsencrypt/:/etc/letsencrypt/
      - /root/nginx/dhparams.pem:/etc/nginx/dhparams.pem
    restart: unless-stopped
    network_mode: host

    image: tomsquest/docker-radicale
    container_name: radicale
    init: true
    read_only: true
      - no-new-privileges:true
      - ALL
      - SETUID
      - SETGID
      - CHOWN
      - KILL
      test: curl -f || exit 1
      interval: 30s
      retries: 3
    restart: unless-stopped
      - /etc/localtime:/etc/localtime:ro
      - /root/radicale/data:/data
      - /root/radicale/config:/config:ro
      - /root/radicale/users:/etc/radicale/users
      - /root/radicale/log:/var/log/radicale/log
    container_name: home-assistant
    image: homeassistant/home-assistant:stable
      - /etc/localtime:/etc/localtime:ro
      - /root/homeassistant:/config
      - TZ=Europe/London
    restart: unless-stopped
    network_mode: host
    image: esphome/esphome
      - /root/esphome/config:/config:rw
      - /etc/localtime:/etc/localtime:ro
    network_mode: host
    restart: always
         24. create nginx.conf file in root home directory this is needed for the nginx container to access the other containers and to have a few needed settings for secure access( edit the duckdns address to suit the url you create on , you will need to create an account and register for a maximum of 5 subdomains) (this if free)

user www-data;
worker_rlimit_core 500M;
worker_processes 1;

events {

  worker_connections 1024;


http {
  map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
  error_log /etc/nginx/error.log warn;
  access_log /etc/nginx/access.log;
  ssl_dhparam /etc/nginx/dhparams.pem;
  ssl_prefer_server_ciphers on;
  ssl_protocols TLSv1.2 TLSv1.3;
  add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
  ssl_stapling on;
  ssl_stapling_verify on;
  ssl_certificate /etc/letsencrypt/live/;
  ssl_certificate_key /etc/letsencrypt/live/;
  ssl_trusted_certificate /etc/letsencrypt/live/;
  ssl_session_cache shared:SSL:10m;
  proxy_buffering off;

  server {
    listen 80;
    return 301 https://$server_name$request_uri;   
  server {
    listen 443 ssl http2;

    location /radicale/ {
    proxy_pass           http://localhost:5232/;
    proxy_set_header     X-Script-Name /radicale;
    proxy_set_header     X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass_header    Authorization;

  server {
    listen 80;
    return 301 https://$server_name$request_uri;

  server {
    listen 443 ssl http2;

    location / {
        proxy_pass http://localhost:8123;
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

     location /api/websocket {
        proxy_pass http://localhost:8123/api/websocket;
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

       25. in the root home directory create an nginx directory, then a cert directory and then create a dhparam file this is used by the nginx container

openssl dhparam -out /root/nginx/cert/dhparam.pem -dsaparam 2048
      26. create a radicale directory , a config directory and add a config file for the radicale container

hosts =

max_connections = 15
# 100 Megabyte
max_content_length = 100000000
# 30 seconds
timeout = 20

# Average delay after failed login attempts in seconds
delay = 600

type = htpasswd
htpasswd_filename = /etc/radicale/users
# encryption method used in the htpasswd file
htpasswd_encryption = md5

filesystem_folder = /data/collections
           27. now we need to install apache2-utils so we can use htpasswd to create a password file for users of Radicale . ( I only use Radicale for calendar sync i.e only one user in this case user calendar, but it can be used for multiple users with calendars , address books and to-do lists for each .)

apt install apache2-utils

htpasswd -c /root/radicale/users calendar

           28. now in order for radicale to work a radicale user and group needs to be created , more info can be gleamed from the provider of the Radicale docker container , here

addgroup --gid 2999 radicale

adduser --gid 2999 --uid 2999 --shell /bin/false --disabled-password --no-create-home radicale

           29. we are almost there now , just a few more steps and some optional ones at the end . Time to install fail2ban and certbot ( certbot obtains a lets encrypt certificate for us and will renew the cert via a cronjob specified later .)

apt install fail2ban certbot
         30. A few necessary adjustments need to be made to fail2ban , lots of articles are available on the on the internet so I wont touch on them here.u Currently I have it working for home assistant but not Radicale , as Radicale doesn't provide logging easily (logging is how fail2ban block unwanted log ins and block the IP address). It is in the works however  and hopefully will be available soon.

cp jail.conf jail.local

nano jail.local
    I tend to edit this section of jail.local to ignore my local network so as not to ban the IP Address of the machine I use to SSH into the Rockpro64 , this is because SSH  Jail is enabled by default on fail2ban .

# "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban
# will not ban a host which matches an address in this list. Several addresses
# can be defined using space (and/or comma) separator.
ignoreip = ::1
          31. I now add 2 files as specified in the home assistant documentation on fail2ban to use the logging from home assistant and what to look(filter) for in the logging .

nano /etc/fail2ban/jail.d/hass.conf

enabled = true
filter = hass
action = iptables-allports[name=HASS]
logpath = /root/homeassistant/home-assistant.log
bantime = 3600
maxretry = 3

nano /etc/fail2ban/filter.d/hass.conf

before = common.conf

failregex = ^%(__prefix_line)s.*Login attempt or request with invalid authentication from <HOST>.*$
ignoreregex =

datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S

         32. A word of warning , if you are starting home assistant from new( as in no existing configuration file yet) I would disable fail2ban for now the reason is once restarted it will complain that there is no log located at /root/homeassistant/home-assistant.log . As stated in home assistants documentation linked above you need to have

    homeassistant.components.http.ban: warning

added to your configuration file  for this to work. If you have an existing home assistant configuration you can copy this across now to a home assistant directory located in the root home directory . add the above logger to the config file and then use the touch command to create the home-assistant.log file . Fail2ban can then be restarted or started if disabled and it wont complain about a missing file and will work when the conatiner is started and begins logging to said file .

systemctl restart fail2ban.service
( change above to start instead of restart if stopped , or change to enable if previously disabled and then start )

now check to see if the jail is up and working

fail2ban-client status hass-iptables

         33. Ok finally certbot , to get certbot to work port 80 and port 443 need to be open on your router and directed to the Rockpro64 IP address (I would also highly recommend that a static IP is set for the rockpro64 in your routers settings also )

lets begin by getting a lets encrypt certificate for the duckdns subdomains associated with our home assisatnt and radicale services.

certbot certonly --standalone -d -d
this will retrieve a certificate and store it for you ready for the nginx container to use .

now certbot can renew this certificate  for all associated subdomains when they have less than 30 days left until expiration this is done with a cronjob

crontab -e
 add this to the now open file

@daily certbot renew --pre-hook "docker-compose -f /root/docker-compose.yml down" --post-hook "docker-compose -f /root/docker-compose.yml up -d"

      34. and finally lets start the containers and run them in the background .

docker-compose up -d
      35. if successful you will see the new containers up and running in portainer at the rockpro64 IP address port 9000 (for example

now you should be able to access the home assistant container at or locally at port 8123)

you can access the Radicale container  via (notice the extension /radicale/ this was specified in the nginx.conf file earlier.) I would suggest  closing the ports on teh router for the time being and accessing home assistant via its local IP address until you have a password and the  setup complete then reopen the ports to access outside of you local network . this is of course not necessary if you have an existing configuration with home assistant and are transitioning to the Rockpro64 server .

      36. the next few steps are optional , I installed Jellyfin  as media server , I did this through the admin panel of SimpNAS under apps  this creates a docker container for you and removes some of the steps to get it to access your media files correctly . of course you can install it manually like we did with the other containers and even use docker-compose by adding the container to the docker-compose.yml file , please note that when certbot renews the certificate it stops all the containers in this file  before renewing the certs this is so port 80 and 443 are open for it to use  and then it restarts the containers again. this will not happen to Jellyfin if installed manually or via the SimpNAS apps

     37 . to top it all off, for those who want the firewall blocking ports on their local network to and from the rockpro64 , this is a little over kill as only port 80 and 443 are open to the outside world via the router, but I understand that some people like to add rules for local traffic to prevent spread locally of a virus or similar between machines  on that network .

I say this because it quite difficult to get a firewall working properly as docker provides it own set of rules to control the firewall and when you add rules of your own it might not work well with dockers rules if they work at all , so as suggested by a great article I found regarding this issue . here is a solution

firstly create a iptables.conf file in /etc/ directory

:FILTERS - [0:0]
:DOCKER-USER - [0:0]


# BASIC Allow
-A INPUT -i lo -j ACCEPT

# Chain to FILTERS


# ALLOW something
-A FILTERS -p all -j ACCEPT -m conntrack --ctstate ESTABLISHED,RELATED
-A FILTERS -p icmp --source -j ACCEPT
-A FILTERS -p tcp --source --dport 22 -j ACCEPT
-A FILTERS -p tcp --source --dport 81 -j ACCEPT
-A FILTERS -p tcp --source --dport 82 -j ACCEPT
-A FILTERS -p tcp --source --dport 139 -j ACCEPT
-A FILTERS -p tcp --source --dport 445 -j ACCEPT
-A FILTERS -p tcp --dport 80 -j ACCEPT
-A FILTERS -p tcp --dport 443 -j ACCEPT
-A FILTERS -p tcp --source --dport 8123 -j ACCEPT
-A FILTERS -p tcp --source --dport 8096 -j ACCEPT
-A FILTERS -p udp --source --dport 1900 -j ACCEPT
-A FILTERS -p udp --source --dport 7359 -j ACCEPT
-A FILTERS -p tcp --source --dport 8000 -j ACCEPT
-A FILTERS -p tcp --source --dport 9000 -j ACCEPT
-A FILTERS -p tcp --source --dport 6052 -j ACCEPT
-A FILTERS -p udp --source --dport 5353 -j ACCEPT
# DENY something

### special cases for servers
### please modify by the server

### end special cases

# Optional logging
#-A FILTERS -m limit --limit 5/min -j LOG --log-prefix "iptables_INPUT_denied: " --log-level 7


and add any rules you want after allow something, those above are just examples using the ports most of the services we have installed, use .

next add these new rules from the file to the kernel to implement them

iptables-restore -n /etc/iptables.conf

and then   create a service that loads this file on startup .

nano /etc/systemd/system/iptables.service

then add this

Description=Restore iptables firewall rules

ExecStart=/sbin/iptables-restore -n /etc/iptables.conf

then enable and start the service

systemctl enable iptables.service
systemctl start iptables.service

I think I've covered most of the setup process for my install , of course you will need to configure the service to suit your individual needs and for that I turn you to there excellent documentation








if you have any questions or feedback for me I will help where I can and edit the tutorial based on giving accurate information .

Possibly Related Threads…
Thread Author Replies Views Last Post
  Tutorial:How to Configure ATS Fan control tuxd3v 26 19,821 11-06-2020, 08:56 PM
Last Post: g_t_j
  Serial Connection Tutorial: FTDI 232RL hmuller 0 425 10-23-2020, 11:56 AM
Last Post: hmuller
  [WIP] backup server (4 sata HDDs + ZFS) rolando 8 3,304 06-30-2020, 10:58 AM
Last Post: dml-pc
  OMV + VPN server + Pi Hole? odhinnhrafn 3 2,038 03-16-2020, 05:01 PM
Last Post: mabs
Video ARMBand an easily configured Home Media Server gabrielsr 2 1,946 07-22-2019, 04:56 AM
Last Post: stuartiannaylor
Wink Tutorial[ Seagate Disks ]: Optimize Life Span/Power Managment tuxd3v 14 6,041 06-19-2019, 04:40 AM
Last Post: Neo2018
Thumbs Up Tutorial[ Seagate Disks ]: Install Seagate OpenSeaChest Utilities tuxd3v 41 21,413 04-08-2019, 10:10 AM
Last Post: Neo2018
  Tutorial[ Multimedia ] : Play DVD movies tuxd3v 0 868 02-26-2019, 07:21 PM
Last Post: tuxd3v
  Tutorial[ OpenCL ]: OpenCL 1.2 FULL_PROFILE Working tuxd3v 0 1,832 11-06-2018, 01:03 PM
Last Post: tuxd3v

Forum Jump:

Users browsing this thread: 1 Guest(s)