Foxws
Stry

Podman Quadlet

stry runs as Podman Quadlet units: config files that let systemd run and manage each container. The units are generated with foxws/laravel-podman, and you manage them day to day with its companion CLI, lpod. For general topics such as customizing presets, secrets or setting up without PHP on the host, see the docs of those two projects. This page only covers what is specific to stry.

Prerequisites

  • Linux with systemd, rootless or system-wide

  • Podman 5.3+ with the quadlet CLI plugin (check that podman quadlet --help works)

  • lpod v2.2.0 or later. Install it once per host, and upgrade it later with lpod self-update. It's a bash script with no dependencies:

    curl -fsSL https://github.com/foxws/lpod/releases/latest/download/install.sh | bash
    

Presets

Each preset is a set of templates in containers/stubs/. You can customize one preset without affecting the others (see Customizing):

Preset Purpose
production The application and its services, see the table below
ondemand Starts the app on its first request, and lets it and its services sleep when idle
development The same services, running against your local code (see Development)
s3 RustFS bucket and CORS setup (see S3)
devcontainer VS Code Dev Containers image (see Development)

production installs these services. Their names start with stry by default; you can change that prefix with PODMAN_QUADLET_PREFIX, which defaults to APP_NAME.

Unit Role
stry Web server for the app (Octane), port 8000
stry-pgsql PostgreSQL
stry-valkey Cache and queue backend
stry-horizon Queue worker
stry-reverb WebSocket server
stry-schedule Scheduler
stry-inertia-ssr Inertia server-side rendering
stry-mailpit Catches mail during development
stry-rustfs S3-compatible storage
stry-typesense Search

Only stry opens a port on the host (8000). The other services are only reachable on the internal {{application}}.network. A few of them need to be reachable from outside (Reverb, RustFS and the Mailpit web UI); the app's built-in Caddy server forwards requests to them based on the hostname. See Reverse Proxy.

Install

php artisan podman:setup   # generates production, ondemand and s3 into podman/{preset}/

# Install every generated service (see the podman/{preset}/ folder for the full list):
lpod install production/app.quadlets --replace
lpod install production/pgsql.quadlets --replace
# ...

# Then set the secrets for each service:
lpod stry secrets
lpod stry-pgsql secrets
# ...

lpod stry up

# Let the app and its services sleep when idle (skip with PODMAN_ONDEMAND_ENABLED=false):
lpod install ondemand/stry-ondemand.socket --replace
lpod idle enable stry

lpod idle enable runs the idle check every minute. Once the app is asleep and has no work left, it stops Horizon and the scheduler timer, so the database and other services can sleep too.

See the package's Quick Start for the full steps.

php artisan podman:setup and podman:generate need foxws/laravel-podman, and so does php artisan podman:idle, which the idle check runs inside the containers. It's a regular dependency, so composer install --no-dev keeps it. lpod doesn't need it; it's the standalone tool from Prerequisites. On a host without PHP, either generate the files elsewhere and copy the podman/ folder over, or run lpod setup to generate them inside a temporary container. See Setting up without PHP on the host for both, and Production Setup for the full stry walkthrough.

In the production preset, the app container uses a pre-built image instead of building one locally. CI builds it and publishes it to ghcr.io/francoism90/stry, and Podman pulls it from there. To use your own registry, set PODMAN_IMAGE_REGISTRY in .env.

Day to day

lpod stry up                    # start
lpod stry shell                 # open a shell
lpod stry artisan migrate       # run Artisan
systemctl --user status stry    # or use systemctl and journalctl directly
journalctl --user -u stry -f

See CLI Interaction for stry's own Artisan commands, and the lpod docs for all lpod commands (secrets, remove, list, print, uninstall and more).

Tuning & hardware acceleration

Resource limits such as Memory= and ShmSize= are set in containers/stubs/production/quadlets/*.quadlets. After changing them, generate the files again (php artisan podman:generate production) and reinstall the service (lpod install ... --replace).

The app image includes the VA-API drivers for Intel and AMD, and the Quick Sync runtime for Intel. By default, horizon.quadlets gives stry-horizon access to /dev/dri for hardware-accelerated transcoding:

[Container]
AddDevice=/dev/dri:/dev/dri
GroupAdd=keep-groups

/dev/dri must exist on the host. On a machine without a GPU, including most cloud and CI machines, stry-horizon won't start until you remove these two lines (see below).

See the FFmpeg hardware acceleration docs for setting up the drivers.

On hosts with SELinux, such as Fedora, rootless Podman can't access /dev/dri by default, even with AddDevice=. Allow it once with:

sudo setsebool -P container_use_devices=true

This setting applies to the whole host, not just one container. Every rootless Podman container on the machine gets access to devices.

To turn off GPU access, for example to force software encoding, remove both lines from containers/stubs/production/quadlets/horizon.quadlets (and from development/quadlets/horizon.quadlets if you use that preset). Then generate the files again and reinstall:

php artisan podman:generate production
lpod install production/horizon.quadlets --replace

NVIDIA

This is untested. It's what NVIDIA GPUs probably need, and we're waiting for feedback from someone who has tried it.

The app image has no NVIDIA libraries, and it doesn't need them. FFmpeg loads libcuda, libnvcuvid (NVDEC) and libnvidia-encode (NVENC) when it runs. These come with the host's NVIDIA driver and must match its version, so the NVIDIA Container Toolkit mounts them into the container. Install the toolkit, then generate the CDI spec once on the host (and again after every driver update):

sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml

In containers/stubs/production/quadlets/horizon.quadlets, replace the /dev/dri device with the GPU:

[Container]
AddDevice=nvidia.com/gpu=all

Set MEDIA_LADDER_HARDWARE=nvenc in .env, then generate the files again and reinstall the service:

php artisan podman:generate production
lpod install production/horizon.quadlets --replace