With TrueNAS Scale 25.04 (“Fangtooth”), iXsystems modernised the App Catalog from the ground up. The previous Kubernetes layer is gone — the system now uses plain Docker Compose as its runtime. The result is leaner, dramatically faster to start, and compatible with virtually any Compose file you find on the internet. For administrators in small and mid-sized companies this means: the NAS becomes a serious platform for custom container workloads, without the need for a separate Docker host or a Proxmox LXC.
In this article we show how the “Custom App” entry lets you deploy your own Docker images from a private registry or Docker Hub, how persistent data lands cleanly inside TrueNAS datasets, and which update workflow has proven itself in production.
What the Custom App in TrueNAS Scale offers
The official App Catalog ships curated applications such as Nextcloud, Plex, Immich or Vaultwarden. For everything that is not in the catalog, the Apps UI provides the “Custom App” entry. It accepts a full Docker Compose YAML and takes care of the entire orchestration — including auto-start after reboot, health checks and direct log access in the web UI.
Under the hood a dedicated Docker daemon runs with its own storage pool. Unlike a manual Docker installation on the old TrueNAS Core, the configuration is now fully managed by the system. OS upgrades no longer overwrite your containers, and the dedicated “apps” pool keeps NAS data and container metadata cleanly separated.
One thing to keep in mind: a Custom App is not just plain Compose, but a slightly extended variant. Networks and a few TrueNAS-specific fields are added automatically if you do not specify them. This simplifies standard cases but can be confusing for very complex stacks.
Preparation: pool, dataset, network
Before deploying the first Custom App, the storage layout should be in place. We recommend the following scheme, which has proven robust in real TrueNAS projects:
| Dataset | Purpose | Recommended properties |
|---|---|---|
tank/apps | Docker root, image layers | recordsize 128K, compression LZ4 |
tank/appdata/<app>/config | Configuration files | recordsize 16K, hourly snapshots |
tank/appdata/<app>/data | Payload, databases | recordsize 64K, daily snapshots |
tank/media | Read-only source e.g. for media servers | shared via SMB/NFS |
The actual apps pool is selected once under “Apps — Configuration”. After that you should create a dedicated dataset under appdata for every application — this gives you per-app snapshots and replication, which is gold during a rollback.
On the network side, the default bridge is enough for most SMB setups. If your TrueNAS sits on multiple VLANs, you can additionally define macvlan networks to attach containers directly with their own IP to management or DMZ segments — nicely paired with a well-segmented OPNsense.
Pulling images from a private registry
The most interesting question in practice is the source of the image. Three scenarios are typical:
- Public image from Docker Hub or GHCR — trivial.
- Private image in a self-hosted registry (e.g. Harbor, Gitea, distribution/registry).
- Custom builds pushed by CI on every commit.
For a private registry, navigate to “Apps — Discover Apps — Manage Container Images — Pull Image” and enter hostname, user and token once. Credentials are stored encrypted on the system and used automatically on every pull. An image like registry.company.lan:5000/internal/timetracker:2.4.1 ends up on the NAS without further steps.
A minimal Custom App entry for a web application with a PostgreSQL backend looks like this:
services:
app:
image: registry.company.lan:5000/internal/timetracker:2.4.1
restart: unless-stopped
environment:
- DATABASE_URL=postgres://app:${DB_PASSWORD}@db:5432/app
- TZ=Europe/Berlin
ports:
- "8080:8080"
volumes:
- /mnt/tank/appdata/timetracker/config:/app/config
- /mnt/tank/appdata/timetracker/uploads:/app/uploads
depends_on:
- db
db:
image: postgres:17-alpine
restart: unless-stopped
environment:
- POSTGRES_USER=app
- POSTGRES_DB=app
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- /mnt/tank/appdata/timetracker/db:/var/lib/postgresql/data
Important: the host paths must exist as proper datasets beforehand. TrueNAS will happily create directories on the fly, but everything then lives on the root dataset of the pool, not in a dedicated ZFS dataset — which makes snapshots useless.
Ports, reverse proxy and TLS
Directly exposed ports such as 8080:8080 are fine for internal testing, but in production you should consistently put a reverse proxy in front. Traefik or Caddy as a second Custom App listening on 80/443 has proven itself; both can auto-discover the other containers via Docker labels.
This produces clean hostnames like timetracker.company.lan, with TLS certificates either from the internal ACME server on the OPNsense or from Let’s Encrypt via DNS challenge. The container itself no longer needs to expose a port on the host — only the internal Docker network is used, which reduces attack surface.
If you would rather not run a reverse proxy, the TrueNAS option “Custom Apps — Network — Host Networking” is available. It shares the host stack with the container and allows privileged ports. For databases or LDAP bridges, this is often the simplest variant.
Update workflow in practice
The image tag is the central lever for updates. We strongly recommend never using latest in Custom Apps, but always pinning a concrete version. This makes rollbacks reproducible and prevents accidental major upgrades when the pool gets pulled overnight.
The typical update run looks like this:
- CI builds a new image, pushes it as
:2.4.2to the registry. - Take a snapshot of the
appdatadataset (manually or via snapshot task). - Edit the Custom App in the TrueNAS UI, raise the tag to
:2.4.2, click “Save”. - TrueNAS pulls the image, replaces the container and restarts automatically.
- On trouble: revert the tag, roll the dataset back to the snapshot.
For multiple apps a small script that uses midclt (the TrueNAS CLI API) to bulk-update image tags pays off. The result is a “GitOps-light” approach that fits nicely with setups where backup and replication via ZFS are already automated.
Limits and recommendations
The Custom App is powerful, but not the right choice for every workload. If you need HA, live migration or multiple nodes, a Proxmox cluster with dedicated VMs is the better platform. Resource-hungry databases or a production Kubernetes platform do not belong on the NAS either. For internal tools, dashboards, monitoring stacks, document workflows and small web applications the Custom App is ideal — the storage is already there, ZFS snapshots protect your data, and operation stays entirely inside the TrueNAS UI.
A word on security: containers in the same Docker network can reach each other. If you need to isolate tenants, define multiple Docker networks or move critical applications to a separate TrueNAS node.
DATAZONE can help
We plan and operate TrueNAS Scale environments for mid-sized companies in Bavaria — from dataset layout and Custom App architecture to integrating a private container registry. If you want to run your own Docker workloads cleanly on the NAS without getting lost in YAML, get in touch: contact us.
More on these topics:
More articles
TrueNAS HA: When Is the Dual Controller Worth It?
Dual-controller high availability on TrueNAS is non-trivial — neither in price nor in concept. When HA really pays off, what it does not solve, and when two single-controller systems are the better choice.
TrueNAS Snapshot Strategies for VM Storage
Specific to VM storage: VM-consistent vs. crash-consistent snapshots, TrueNAS vs. hypervisor snapshots, dataset layout per VM or per pool, VM-aware backup, restore paths, retention trade-offs.
iSCSI vs. NFS for Hypervisor Storage: When What?
iSCSI (block) vs. NFS (file) as VM storage for VMware, Proxmox and KVM. Performance, snapshots, multipath, cluster behaviour — a concrete decision guide for which protocol fits which workload.