Personal media server that organizes and streams your movie, TV, and music collections to all your devices.
| Registry | ghcr.io/daemonless/plex |
| Source | https://github.com/daemonless/plex |
| Website | https://plex.tv/ |
| Tag | Description | Best For |
|---|---|---|
latest |
Upstream Binary. Built from official release. | Most users — recommended. |
Before deploying, ensure your host environment is ready. See the Quick Start Guide for host setup instructions.
services:
plex:
image: "ghcr.io/daemonless/plex:latest"
container_name: plex
environment:
- PUID=1000 # User ID for the application process
- PGID=1000 # Group ID for the application process
- TZ=UTC # Timezone for the container
- VERSION=container # Plex update channel (container, public, plexpass)
- PLEX_CLAIM= # Optional: Claim token — get one at https://plex.tv/claim
volumes:
- "/path/to/containers/plex:/config"
- "/path/to/containers/plex/transcode:/transcode" # optional
- "/path/to/movies:/movies"
- "/path/to/tv:/tv"
# always (not unless-stopped) so FreeBSD's podman rc.d auto-starts it at boot
restart: alwaysSave as compose.yaml, then run podman-compose up -d.
.env:
# .env
DIRECTOR_PROJECT=plex
PUID=1000
PGID=1000
TZ=UTC
VERSION=container
PLEX_CLAIM=
appjail-director.yml:
# appjail-director.yml
options:
- virtualnet: ':<random> default'
- nat:
services:
plex:
name: plex
options:
- container: 'boot args:--pull'
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
- VERSION: !ENV '${VERSION}'
- PLEX_CLAIM: !ENV '${PLEX_CLAIM}'
volumes:
- plex: /config
- plex_transcode: /transcode
- movies: /movies
- tv: /tv
volumes:
plex:
device: '/path/to/containers/plex'
plex_transcode:
device: '/path/to/containers/plex/transcode'
movies:
device: 'movies'
tv:
device: 'tv'Makejail:
# Makejail
ARG tag=latest
OPTION overwrite=force
OPTION from=ghcr.io/daemonless/plex:${tag}
Save the files above, then run appjail-director up.
podman run -d --name plex \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=UTC \
-e VERSION=container \
-e PLEX_CLAIM= \
-v /path/to/containers/plex:/config \
-v /path/to/containers/plex/transcode:/transcode # optional \
-v /path/to/movies:/movies \
-v /path/to/tv:/tv \
ghcr.io/daemonless/plex:latestSave as run.sh, then run sh run.sh.
appjail oci run -Pd \
-o overwrite=force \
-o container="args:--pull" \
-o virtualnet=":<random> default" \
-o nat \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=UTC \
-e VERSION=container \
-e PLEX_CLAIM= \
-o fstab="/path/to/containers/plex /config <pseudofs>" \
-o fstab="/path/to/containers/plex/transcode /transcode <pseudofs>" \ # optional
-o fstab="/path/to/movies /movies <pseudofs>" \
-o fstab="/path/to/tv /tv <pseudofs>" \
ghcr.io/daemonless/plex:latest plexSave as run.sh, then run sh run.sh.
Warning
Bastille's OCI support is experimental. It requires buildah, shares the host network stack (inherit), and persists image-declared volumes under --data-path.
services:
plex:
image: "ghcr.io/daemonless/plex:latest"
container_name: plex
network_mode: host # jail shares host networking
environment:
- PUID=1000
- PGID=1000
- TZ=UTC
- VERSION=container
- PLEX_CLAIM=Save as podman-compose.yml, then run bastille up. Or via CLI:
bastille create -O \
--env PUID=1000 \
--env PGID=1000 \
--env TZ=UTC \
--env VERSION=container \
--env PLEX_CLAIM= \
--data-path /path/to/containers/plex \
plex ghcr.io/daemonless/plex:latest inherit- name: Deploy plex
containers.podman.podman_container:
name: plex
image: "ghcr.io/daemonless/plex:latest"
state: started
restart_policy: always
env:
PUID: "1000"
PGID: "1000"
TZ: "UTC"
VERSION: "container"
PLEX_CLAIM: ""
volumes:
- "/path/to/containers/plex:/config"
- "/path/to/containers/plex/transcode:/transcode" # optional
- "/path/to/movies:/movies"
- "/path/to/tv:/tv"Save as plex-deploy.yaml, then run ansible-playbook plex-deploy.yaml.
| Variable | Default | Description |
|---|---|---|
PUID |
1000 |
User ID for the application process |
PGID |
1000 |
Group ID for the application process |
TZ |
UTC |
Timezone for the container |
VERSION |
container |
Plex update channel (container, public, plexpass) |
PLEX_CLAIM |
`` | Optional: Claim token — get one at https://plex.tv/claim |
| Path | Description |
|---|---|
/config |
Configuration directory |
/transcode |
Transcode directory (Optional) |
/movies |
Movie library |
/tv |
TV series library |
Plex requires network_mode: host on FreeBSD Podman. Bridge networking causes the Plex setup
wizard to reject all connections with "Not authorized" because it only allows access from
127.0.0.1, and bridge networking makes all connections appear to come from the gateway IP.
With host networking, ports are bound directly on the host — no ports: mapping needed.
The Plex setup wizard must be accessed from localhost. After starting the container, create
an SSH tunnel from your local machine:
ssh -L 32400:localhost:32400 <your-host>
Then open http://localhost:32400/web in your browser to complete setup.
Architectures: amd64
User: bsd (UID/GID via PUID/PGID, defaults to 1000:1000)
Base: FreeBSD 15
Need help? Join our Discord community.