Here is how you deploy Invidious via Podman 5.x or higher. All commands are executed as a normal user, if you want to use root then you need to modify some paths. Root-less containers are preferred together with SELinux in enforcing mode for maximum security.

Create a new volume for database:

podman volume create invidious-db

Start a temporary container:

podman run --rm -it --name invidious-init -v invidious-db:/var/lib/postgresql/data:Z -p 5432:5432 -e POSTGRES_DB=invidious -e POSTGRES_USER=kemal -e POSTGRES_PASSWORD=kemal docker.io/library/postgres:14

In another terminal, migrate the database:

export PGPASSWORD=kemal
for F in channels videos channel_videos users session_ids nonces annotations playlists playlist_videos; do
    curl -s https://raw.githubusercontent.com/iv-org/invidious/refs/heads/master/config/sql/$F.sql | \
        psql -h localhost -p 5432 -U kemal invidious
done

Shutdown the temporary container, it is no longer needed. Create database volume unit:

cat > ~/.config/containers/systemd/invidious-db.volume <<EOF
[Volume]
VolumeName=invidious-db
EOF

And a database container:

cat > ~/.config/containers/systemd/invidious-db.container <<EOF
[Container]
ContainerName=invidious-db
Environment=POSTGRES_DB=invidious POSTGRES_USER=kemal POSTGRES_PASSWORD=kemal
Image=docker.io/library/postgres:14
HealthCmd=pg_isready -h localhost -p 5432 -U kemal -d invidious
Notify=healthy
Pod=invidious.pod
Volume=invidious-db.volume:/var/lib/postgresql/data:Z
EOF

Create a helper container:

cat > ~/.config/containers/systemd/invidious-sig-helper.container <<EOF
[Container]
ContainerName=invidious-sig-helper
Environment=RUST_LOG=info
Image=quay.io/invidious/inv-sig-helper:latest
Exec=--tcp 0.0.0.0:12999
Pod=invidious.pod
EOF

Now, generate your VISITOR_DATA an PO_TOKEN secrets, run this command and wait until it prints both values:

podman run quay.io/invidious/youtube-trusted-session-generator

Set those secrets as temporary environmental variables, also generate a random string for HMAC secret:

HMAC=$(openssl rand -base64 21)
VISITOR_DATA="ABCDEF%3D%3D" # notsecret
PO_TOKEN="MpOIfiljfsdljds-Lljfsdk-ojrdjXVs==" # notsecret

In the same terminal where you defined the environmental variables, create new environmental config file:

cat > ~/.config/containers/systemd/invidious.env <<EOF
INVIDIOUS_DATABASE_URL="postgres://kemal:kemal@invidious-db:5432/invidious"
#INVIDIOUS_CHECK_TABLES=true
#INVIDIOUS_DOMAIN="inv.example.com"
INVIDIOUS_SIGNATURE_SERVER="invidious-sig-helper:12999"
INVIDIOUS_VISITOR_DATA="$VISITOR_DATA"
INVIDIOUS_PO_TOKEN="$PO_TOKEN"
INVIDIOUS_HMAC_KEY="$HMAC"
EOF

And create an invidious container unit:

cat > ~/.config/containers/systemd/invidious.container <<EOF
[Container]
ContainerName=invidious
EnvironmentFile=%h/.config/containers/systemd/invidious.env
Image=quay.io/invidious/invidious:latest
Pod=invidious.pod
[Unit]
After=invidious-db.service
EOF

And finally, create a pod unit. Note only port 3000 is exposed, do not expose other ports!

cat > ~/.config/containers/systemd/invidious.pod <<EOF
[Pod]
PodName=invidious
PublishPort=3000:3000
[Install]
WantedBy=multi-user.target default.target
EOF

Systemd units are generated on-the-fly during daemon-reload command, but before that let’s check syntax with quadlet generator. Note, you need Podman version 5.0 or higher, older versions will not work:

/usr/libexec/podman/quadlet -dryrun -user

Reload systemd daemon. Keep in mind you need to do this command every time you change any unit file, you can change the environmental file without reload tho.

systemctl --user daemon-reload

And the whole application can be now started:

systemctl --user start invidious-pod

Keep in mind that generated units cannot be enabled using systemctl enable, the main pod will be enabled automatically. If you do not like this behavior, remove the WantedBy line from invidious.pod.

Head over to your instance at http://inv.example.com:3000 and have fun!