Post

How I Run Docker on TrueNAS Like a Pro

Learn how to run Docker apps on TrueNAS like a pro — with full control, clean YAML, and no extra dashboards.

Have questions or a different setup that works for you? Leave a comment, I’d love to hear how others are running Docker on TrueNAS.

📺 Watch Video

The Approach

We’re going to:

  • Create a dataset to store our containers and configs
  • Use Docker Compose and .env files
  • Optionally manage everything from a browser using Code Server

Example 1: Basic Stateless Container (Draw.io)

First make the dataset drawio in the TrueNAS UI.

Then create the compose.yaml file

1
nano compose.yaml

drawio docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
---
services:
  drawio:
    image: jgraph/drawio
    container_name: drawio
    restart: unless-stopped
    ports:
      - 9080:8080
      - 9443:8443
    environment:
      - PUBLIC_DNS=${PUBLIC_DNS:-domain}
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8080 || exit 1"]
      interval: 5s
      timeout: 5s
      retries: 5
      start_period: 10s

Then add it to your includes: while creating a Custom App based on YAML.

Example 2: NGINX with Volume Mounts

First make the dataset nginx in the TrueNAS UI.

Then create the compose.yaml file

1
nano compose.yaml

nginx docker-compose.yml

1
2
3
4
5
6
7
8
---
services:
  nginx:
    image: nginx:1-alpine
    ports:
      - 8099:80
    volumes:
      - ./html/:/usr/share/nginx/html

Create the html folder

1
mkdir html

inside of there create an index.html file

1
2
cd html
nano index.html

nginx index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Hello World App</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    body {
      font-family: sans-serif;
      display: flex;
      height: 100vh;
      justify-content: center;
      align-items: center;
      background-color: #f0f0f0;
      margin: 0;
    }
    h1 {
      color: #333;
    }
  </style>
</head>
<body>
  <h1>Hello, World!</h1>
</body>
</html>

Then add it to your includes: while creating a Custom App based on YAML.

Example 3: Code Server for Web-Based Editing

First make the dataset code-server in the TrueNAS UI.

Then create the compose.yaml file

1
nano compose.yaml

code-server docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---
services:
  code-server:
    image: lscr.io/linuxserver/code-server:latest
    container_name: code-server
    env_file:
      - .env
    environment:
      - PUID=${PUID:-950} #update with your userId or update .env
      - PGID=${PGID:-950} #update with your groupId or update .env
    volumes:
      - ./config:/config
      - /mnt/storage0/nginx/:/nginx
      - /mnt/storage0/code-server/:/code-server
      - /mnt/storage0/drawio/:/drawio

    ports:
      - 8443:8443
    restart: unless-stopped

Then create the .env file

1
nano .env
1
2
3
PUID=950
PGID=950
TZ=America/Chicago

Then add it to your includes: while creating a Custom App based on YAML.

Bonus: Run Home Assistant with macvlan and Traefik on TrueNAS

If you’re self-hosting Home Assistant on TrueNAS, and you want:

  • Native device discovery on your LAN (or VLAN!)
  • Reverse proxy access through your domain
  • A clean, Compose-managed setup

Then this combo of Docker Compose + macvlan + Traefik is exactly what you’re looking for.

Note: This setup is designed for users with a single NIC. I have it connected to a bond in my homelab, which works fine, but I just want to keep it simple for this example. Also, you can host your Home Assistant on one network or VLAN while having your TrueNAS on another (that’s how I do it).


Why macvlan?

By using macvlan, you give your Home Assistant container a LAN IP, just like a physical device. This enables:

  • Proper device discovery (e.g., Google Home, Shelly, Zigbee)
  • Direct network communication with other LAN devices
  • Better compatibility with smart home protocols

What You Need

Assumptions for this setup:

  • Your NIC is eth0
  • Your LAN subnet is 192.168.20.0/24
  • Your gateway is 192.168.20.1
  • You want Home Assistant to have IP 192.168.20.202
  • You’re using Traefik as a reverse proxy (but optional)

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
services:
  homeassistant:
    container_name: homeassistant
    image: ghcr.io/home-assistant/home-assistant:stable
    pull_policy: always
    restart: unless-stopped
    env_file:
      - .env
    security_opt:
      - no-new-privileges:true
    networks:
      iot_macvlan:
        ipv4_address: 192.168.20.202
      traefik:
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - /mnt/storage0/home-assistant/config:/config
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.homeassistant.rule=Host(`homeassistant.yourdomain.com`)"
      - "traefik.http.routers.homeassistant.entrypoints=https"
      - "traefik.http.routers.homeassistant.tls=true"
      - "traefik.http.routers.homeassistant.tls.certresolver=cloudflare"
      - "traefik.http.services.homeassistant.loadbalancer.server.port=8123"

networks:
  iot_macvlan:
    external: true
  traefik:
    external: true

Note: The iot_macvlan and traefik networks must already exist as external Docker networks. You can create the macvlan network using the command below.


Create the macvlan Network

You only need to create the macvlan network once:

1
2
3
4
5
docker network create -d macvlan \
  --subnet=192.168.20.0/24 \
  --gateway=192.168.20.1 \
  -o parent=eth0 \
  iot_macvlan

Warning: macvlan works best with physical interfaces like eth0. It can work with bonded or VLAN interfaces too, but compatibility depends on your network setup and driver support. (I am using a bond without ay issues but worth mentioning in case you run into issues)

Danger: If you assign the same IP to more than one container or device, it will cause an IP conflict and could take your Home Assistant or host offline.


macvlan + Traefik

SettingValueDescription
Interfaceeth0Your physical NIC or bond that has access to tagged packets
Subnet192.168.20.0/24Matches your LAN range
Gateway192.168.20.1Gateway IP
Home Assistant IP192.168.20.202LAN IP assigned to the container
Domainhomeassistant.yourdomain.comUsed in your Traefik rule
Networksiot_macvlan, traefikMust exist as external networks, created with the docker command

Results

With this setup:

  • Home Assistant has a dedicated IP on your LAN
  • You can access it securely via Traefik + HTTPS
  • Everything is defined in Compose.

Bonus: Fix Custom Icons for TrueNAS Custom Apps

By default, when you deploy a custom app on TrueNAS using your own Docker Compose, the app will likely show up with a missing or blank icon in the Apps UI.

Fortunately, you can fix that by editing the app’s metadata file.


Update the App Metadata

Edit the following file on your TrueNAS system:

1
/mnt/.ix-apps/app_configs/YOUR_APP_NAME/metadata.yaml

Add the icon: property inside the metadata: block. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
custom_app: true
human_version: 1.0.0_custom
metadata:
  app_version: custom
  capabilities: []
  description: This is a custom app where user can use his/her own docker compose
    file for deploying services
  home: ''
  host_mounts: []
  maintainers: []
  name: custom-app
  run_as_context: []
  sources: []
  title: Custom App
  train: stable
  version: 1.0.0
  icon: https://media.sys.truenas.net/apps/homepage/icons/icon.png
migrated: false
notes: null
portals: {}
version: 1.0.0

Note: You will need to redeploy the app and refresh the UI for the icon to show up.


Mount App Configs in Code Server

If you’re using Code Server like I am, you can easily edit app configs like this by mounting the app config directory into your container:

1
2
volumes:
  - /mnt/.ix-apps/app_configs/:/ix-apps

This makes /ix-apps accessible from inside Code Server, so you can quickly edit metadata, YAML, or other settings.

Bonus: Embed Icons with Base64 (No Internet Needed)

If you’re customizing app metadata on TrueNAS SCALE, you can embed an image directly using a Base64-encoded Data URL. This is perfect if you don’t want your system to rely on the internet just to load icons.

This method was originally shared in this excellent TrueNAS forum thread: How to change icon of custom app


Option 1: Encode via Command Line (JPEG Example)

Linux:

1
base64 -w 0 your-image.jpg > output.txt

macOS:

1
base64 your-image.jpg > output.txt

Then embed it into your app’s metadata like this:

1
icon: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...'

Replace ... with the full Base64 string from output.txt


Option 2: Use a Web Tool

Use base64.guru/converter/encode/image

  • Upload your image (JPEG/PNG/etc.)
  • Choose “Data URI” as the output format
  • Copy the full result into your app’s metadata:
1
icon: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...'

Why This Is Useful

  • Keeps apps fully offline-capable
  • Avoids pulling icons from GitHub, Docker Hub, or external URLs
  • Ideal for self-contained deployments on isolated or air-gapped TrueNAS SCALE systems

Join the conversation

🛍️ Check out the new Merch Shop at https://l.technotim.live/shop

⚙️ See all the hardware I recommend at https://l.technotim.live/gear

🚀 Don’t forget to check out the 🚀Launchpad repo with all of the quick start source files

🤝 Support me and help keep this site ad-free!

This post is licensed under CC BY 4.0 by the author.