Remote access with Docker

Edit 19 Jan 2024

I wrote this back in 2001 and was thinking was an update required but I am glad to know that people have reported this is still valid.

You shouldn’t have to wade through everybodies comments to get things working.

Often, people report that their connection is reported by the browser as unsafe. I think this is caused by the Let’s encrypt certificate limits. I wait and it eventually goes away. If necessary, read this, but I stick with Letsencrypt.

As remote access exposes your machine to the outside world, security worried me.
The solution I use is something called Crowdsec. I started to write a guide, but didn’t feel satisfied with it as I couldn’t properly summarise what was going on. In brief it can analyse logs (both linux and home assistant) , checks suspicious behaviour and blocks it. Attackers are reported to a central server and your system recieves and blocks the crowdsourced attackers. The linuxserver.io guide provides detailed explanations.


Introduction

For those of us who can’t ( or don’t want to) run the supervised system, getting remote access to Home Assistant without the add-ons seemed to be a nightmare.

But there is real simple way to get everything done, including Letsencrypt, NGINX, certificate renewal, duckdns, security etc.

It is a docker package called SWAG and it includes a sample home assistant configuration file that only need a few tweaks.

A basic understanding of Docker is presumed and Docker-Compose is installed on your machine.

The configuration is minimal so you can ​get the test system working very quickly. After that, it should be easy to modify your existing configuration.

Rather than upset your production system, I suggest you create a test directory; /home/user/test.
Also, create the data volumes so that you own them;

/home/user/volumes/hass
/home/user/volumes/swag

1) Port Forwarding

Forward ports 80 and 443 through your router to ports 80 and 443 on your server.
No need to forward port 8123

2) DuckDNS

Set up a Duckdns account. This is simple and fully explained on their web site.
Keep a record of “your-domain” and “your-access-token”.

3) docker-compose.yml

The config below is the basic for home assistant and swag.

The first service is standard home assistant container configuration.
Note that Network mode is “host” which now means Docker port mapping is not allowwed; I comment them out.

The second service is swag.
Since docker creates some files as root, you will need your PUID & GUID; just use the Unix command ‘id’ to find these.
Change your duckdns info.

The third part fixes the docker network so it can be trusted by HA.

version: "3"
services:
  hass:
    container_name: hass
    image: homeassistant/home-assistant
    volumes:
      - /home/USER/volumes/hass:/config
      - /etc/localtime:/etc/localtime:ro
#    ports:
#     - 8123:8123
    network_mode: host
    restart: unless-stopped

swag:
    image: ghcr.io/linuxserver/swag
    container_name: swag
    cap_add:
      - NET_ADMIN
    environment:
      - PUID=1001
      - PGID=1001
      - TZ=Europe/Paris
      - URL=YOUR-DOMAIN.duckdns.org
      - VALIDATION=duckdns
      - DUCKDNSTOKEN=YOUR-TOKEN
      - SUBDOMAINS=wildcard
    volumes:
      - /home/USER/volumes/swag:/config
#    ports:
#     - 443:443
#     - 80:80
    restart: unless-stopped


# set trusted docker internal network
networks:
  default:
      ipam:  
        config: 
         - subnet: 172.10.0.0/24

4) Set up SWAG

Run
docker-compose up swag

This will down load the swag image, create the swag volume, unpack and set up the default configuration.
It takes a some time to generate the certificates etc

If all goes well, it should end with

swag | [services.d] starting services
swag | [services.d] done.
swag | Server ready.

Use ctrl-c to stop docker gracefully.

5) Set up Homeassistant

Run

docker-compose up homeassistant

When it is done, use ctrl-c to stop docker gracefully.

6) SWAG configuration for Homeassistant

The SWAG container contains a standard (NGINX) configuration sample file for home assistant;

homeassistant.subdomain.conf.sample

Rename it to
homeassistant.subdomain.conf

Note: It is found in /home/user/test/volumes/swag/nginx/proxy-confs/

Normally, docker would know the IP address of homeassistant, but since we use ‘net mode’, the IP address of your server needs to be given in two places:
set $upstream_app homeassistant;
to your host IP address
set $upstream_app 192.168.X.XXX;

This is the homeassistant.subdomain.conf file (with all #comments removed for clarity)

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name homeassistant.*;
    include /config/nginx/ssl.conf;
    client_max_body_size 0;
    location / {
        include /config/nginx/proxy.conf;
        include /config/nginx/resolver.conf;
        set $upstream_app 192.168.XX.XXX;
        set $upstream_port 8123;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;
    }
        location /api {
        include /config/nginx/proxy.conf;
        include /config/nginx/resolver.conf;
        set $upstream_app 192.168.XX.XXX;
        set $upstream_port 8123;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;
    }
}

7) HA config.yaml

We need to keep our ip address in duckdns uptodate. ( Note: SWAG will do this and it is not really necessary to ask home assistant to do it aswell)

Add the following to you home assistant config.yaml ( /home/user/test/volumes/hass/configuration.yaml)

Adjust for your local lan network and duckdns info

http:
  ip_ban_enabled: true
  login_attempts_threshold: 3
  use_x_forwarded_for: true
  trusted_proxies:
    - 192.168.X.0/24  # Local Lan
    - 172.10.0.0/24  # Docker network

duckdns: 
  domain: "YOUR-DOMAIN"
  access_token: "YOUR-TOKEN"

Note: If you use Crowdsec security, the ip_ban_ enabled should be false or you won’t be reporting attackers.

8) Finally-Test it is working

Launch homeassistant and swag

docker-compose up -d

Wait a little.

Then, use your browser to logon from your local network 192.168.X.XXX:8123 and you should get your normal home assistant login.

Finally, use your browser to logon from outside your home
https://homeassistant.YOUR-SUB-DOMAIN.duckdns.org

Note: unless your router supports ’ loopback’ ( and mine didn’t) you might not be able to connect; in that case use a telephone ( or tor browser) rather than your local LAN connection.

That’s it. You have remote access to home assistant.

Footnotes:

You must include homeassistant in the address when you connect.
If you use just https://
YOUR-SUB-DOMAIN.duckdns.org ( which defaults to https://www.YOUR-SUB-DOMAIN.duckdns.org ), you will get taken to the default SWAG web page of your server.

17 Likes

I’m sure you have your reasons for using docker. However I want to point out that using a virtual box (in my experience) has been such a fluid experience

Also I’m guessing that you can’t get supervisor addons in docker

If you can get supervisor addons in docker, use WireGuard, its amazing

If you have a windows server, you can use the link bellow, using the VirtualBox (.vdi) image choice

I fully agree. I would use the supervised system or a virtual machine if I could.
I tried installing hassio over Ubuntu, but ran into problems.
In a first draft, I started my write up with this observation, but removed it to keep things brief.

I use home assistant container and swag in docker too. I think the best benefit is I can run several other containers and programs, including a Shinobi NVR, on the same machine. That doesn’t seem possible with hass.io, and anyone trying to install any of the other supervised versions on linux always seems to have problems. It is more complex and you don’t get the add-ons, but there are a lot more options. You just have to run add-ons, like Node Red, in their own docker containers and manage them yourself.
Also, here is a good write up I used to set up the Swag/NGINX proxy, with similar steps you posted above Nginx Reverse Proxy Set Up Guide – Docker

1 Like

Thanks, I don’t need another containers ( yet…), just a way to get remote access for my Smartthings. But yes it looks as if you can easily add in lots of stuff. I had previously followed an earlier (dehydrated) guide for remote access and it was complicated…
Reading through the good link you gave; there is no mention that swag is already configured and a simple file rename suffices.

Yes I definitely like the option to keep it simple, but I’ve found a lot with Home Assistant trying to take shortcuts generally has a downside that you only find out about later. The official home assistant install documentation advises home assistant container needs to be run with the --network=host option to be a supported install versus just mapping port 8123. Doing that then makes the container run with the network settings of the same machine it is hosted on. Without using the --network=host option auto discovery and bluetooth will not work in Home Assistant. The Smartthings integration doesn’t need autodiscovery so if that’s all you’re really using it for you’ll be fine, but definitely can run into issues trying to setup other integrations later that need either autodiscovery or upnp to work.

Once you do the --host option though, the Home Assistant container isn’t a part of the docker network anymore and it basically makes the default config in the swag container not work out of the box (unless they fixed it recently) and complicates the setup beyond the nice simple process you noted above. So then its pick your poison - not having autodiscovery working or not having your homeassistant container on the docker network. However if you update the config based on the post I linked above from @juan11perez to make everything work together you can have your cake and eat it too (use host network mode and get the swag/reverse proxy working), although it is a lot more complicated and more work. I also have fail2ban working using his setup/config so not sure why that didn’t work in your setup.

One other thing is that to overcome the root file permission issue and avoid needing to run a chown, you can set the PUID and PGID environment variables to the non-root user of the machine, which will be generally 1000. That way any files created by the swag container will have the same permissions as the non-root user. A lot of times when you don’t set these variables and you use chown, when you restart the container the files will just go back to belonging to root and you’ll have to chown them again to get access to them - Understanding PUID and PGID - LinuxServer.io

1 Like

Thanks,

but I am still unsure what installation you are running cause you had called it hass.

I have a pi-4 running raspbian in a container and so far it had worked out for me over the past few weeks where I had implemented a lot of sensors and devices of various brands and also done the tuya local and energy meter integrations beyond the xiaomi, SonOff and smartlife stuff.

I have a duckdns account and i know a bit about the docker configuration, how to start and so on, but that is it (beyond the usual router stuff).

Can I take your guideline from top to bottom to get duckdns or the swag container running and working with my existing system ?
I do not care about crashing the system cause I have a nightly images and on top a daily HA backup so that I can back on track easily if I ever crash my system.

Right now my HA is LAN or WLAN only and every remote actions can only be achieved via VNC access on the Pi 4 VNC server or a client Mini PC that is running chrome and so on.

Where do I have to be carefull to not get it wrong?

As you had said I am that typical newbie who had a raspbian / pi OS experience and had made his first steps in the HA environment.
thx for your idea for that guideline

Hi.
Hass for me is just a shortcut for home-assistant.
If you are running on a pi, I thought most people run the Home Assistant Operating System which has add-ons for remote access.
If you are running home assistant inside a docker container, then I see no reason why my guide shouldn’t work.
The great thing about pi is you can easily switch out the SD card instead of a test directory and give it a try; it shouldn’t take long.

I just wanted to make sure what Hass means in this context cause for me it is the HASSIO image running on pi alone , but I do not wanna have a pure HA on a pi 4 that can not do anything else.

I have a basic Pi OS4 running / updating and when I could not get the HA to run under PI OS4 cause there was a pyhton ssl error nightmare on a fresh setup I went for the docker way just to be sure that I can use my Pi 4 for something else cause HA is not doing that much the whole day if I look at the cpu running at 8% incl. my pihole and some minor other things like VNC server.

So I will follow the guide line and hope for the best that it fits for my basic docker cause I have not changed anything on that docker since I installed it.

Good luck. Let us know if all is ok or not.

So did it work out for you?

Hi, thank you for this guide.
BTW there is no need to expose 80 port since you use VALIDATION=duckdns.
Forwarding 443 is enough.

:grinning: Thanks, yes no need to forward port 80. l wasn’t quite sure, so I left in in

Very nice guide, thanks Bry! I do run into an issue while accessing my homeassistant
instance from outside of my network. It seems to register that there is a swag instance running on my address, but this is of course what I would like to see, I would like to be able to access my homeassistant instance from outside. image

Attached are my:
Docker-compose.yml

version: '3'
services:
  homeassistant:
    container_name: homeassistant
    build:
      context: ./dockerfiles/homeassistant/
      dockerfile: Dockerfile
    volumes:
      - /PATH_TO_YOUR_CONFIG/:/config/	
      - /etc/localtime:/etc/localtime:ro
    restart: unless-stopped
    privileged: true
    network_mode: host
    ports:
      - 8123:8123
  deconz:
    image: marthoc/deconz:stable
    container_name: deconz
    network_mode: host
    restart: always
    volumes:
      - /dev/ttyACM0:/dev/ttyACM0
      - /etc/localtime:/etc/localtime:ro
      - /opt/deconz:/root/.local/share/dresden-elektronik/deCONZ
    devices:
      - /dev/ttyACM0
    environment:
      - DECONZ_WEB_PORT=80
#      - DECONZ_WS_PORT=443
      - DEBUG_INFO=1
      - DEBUG_APS=0
      - DEBUG_ZCL=0
      - DEBUG_ZDP=0
      - DEBUG_OTAU=0

  swag:
    image: ghcr.io/linuxserver/swag
    container_name: swag
    cap_add:
      -  NET_ADMIN
    environment:
      -  PUID=1000
      -  PGID=1000
      -  TZ=Europe/Paris
      -  URL=xxx
      -  VALIDATION=duckdns
      -  DUCKDNSTOKEN=xxx
      -  SUBDOMAINS=wildcard
    volumes: 
      -  /home/USER/volumes/swag:/config
    ports:
      -  443:443
      -  80:80
    restart: unless-stopped

networks:
  default:
    ipam:
      config:
        - subnet: 172.10.0.0/24

homeassistant.subdomain.conf

## Version 2021/07/13
# make sure that your dns has a cname set for homeassistant and that your homeassistant container is not using a base url

# As of homeassistant 2021.7.0, it is now required to define the network range your proxy resides in, this is done in Homeassitants configuration.yaml
# https://www.home-assistant.io/integrations/http/#trusted_proxies
# Example below uses the default dockernetwork ranges, you may need to update this if you dont use defaults.
#
# http:
#   use_x_forwarded_for: true
#   trusted_proxies:
#     - 172.16.0.0/12

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name homeassistant.*;

    include /config/nginx/ssl.conf;

    client_max_body_size 0;

    # enable for ldap auth, fill in ldap details in ldap.conf
    #include /config/nginx/ldap.conf;

    # enable for Authelia
    #include /config/nginx/authelia-server.conf;

    location / {
        # enable the next two lines for http auth
        #auth_basic "Restricted";
        #auth_basic_user_file /config/nginx/.htpasswd;

        # enable the next two lines for ldap auth
        #auth_request /auth;
        #error_page 401 =200 /ldaplogin;

        # enable for Authelia
        #include /config/nginx/authelia-location.conf;

        include /config/nginx/proxy.conf;
        include /config/nginx/resolver.conf;
        set $upstream_app 192.168.178.xx;
        set $upstream_port 8123;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;

    }

    location /api {
        include /config/nginx/proxy.conf;
        include /config/nginx/resolver.conf;
        set $upstream_app 192.168.178.xx;
        set $upstream_port 8123;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;
    }

    location /local {
        include /config/nginx/proxy.conf;
        include /config/nginx/resolver.conf;
        set $upstream_app homeassistant;
        set $upstream_port 8123;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;
    }
}

and my configuration.yml

# Remote access settings for duckDNS and swag
http:
  ip_ban_enabled: true
  login_attempts_threshold: 3
  use_x_forwarded_for: true
  trusted_proxies:
    -  192.168.178.0/24 # Local area network
    -  172.10.0.0/24 # Docker network
  
duckdns:
  domain: xx
  access_token: xx

As a privacy measure I removed some of my addresses with one or more Xs.

What is going wrong? Could anyone help me understand this problem.

Thanks so far!

hi,
I trust you are trying to connect with https://homeassistant.your-sub-domain.duckdns.org/ not just https://your-sub-domain.duckdns.org/

For me, the second option took me to the web server.
It looks as if the swag version you are using is newer than mine.
Sorry, I am away from home at present and have other occupations, so I can’t give more help now

Home assistant runs in “host networking” mode, and you can’t reference a container running in host networking mode by its container name in an nginx config. Try replacing homeassistant on this line with your ip address 192.168.178.xx like on the other lines.

Not sure if that will fix it. Check your logs in config/log/nginx. Look at the access and error logs, and try posting any errors.

Also, any errors show in the homeassistant logs about a misconfigured proxy?

Great guide! Works like a charm.

Note that the ports statment in the docker-compose file is unnecessary since home assistant is running in host network mode. And with docker-compose version 1.28 leaving it in results in an error and the container does not start. Just remove the ports section to fix the error.

Excellent work, much simpler than my previous setup without docker! Does this automatically renew the certificate and restart everything that need to be restarted, or does it require any manual handling?

Cert renewal with the swag container is automatic - its checked nightly and will renew the certificate automatically if it expires within 30 days. Per the documentation:

Certs are checked nightly and if expiration is within 30 days, renewal is attempted. If your cert is about to expire in less than 30 days, check the logs under /config/log/letsencrypt to see why the renewals have been failing. It is recommended to input your e-mail in docker parameters so you receive expiration notices from Let’s Encrypt in those circumstances.

I’ve been using it for almost a year and never had a cert not renew properly - so for me at least this is handled very well. I’m using duckdns with a wildcard cert.

This was super helpful, thank you! Everything is up and running now, though I had to use a different IP range for the docker network.

One question: what’s the best way to keep my ip updated with duckdns? The swag docs suggests using the duckdns container, but could a simple cron job do the trick? e.g. https://www.slashlogs.com/how-to-update-your-duckdns-ip-automatically-from-your-raspberry-pi/