Providing the Demo Video¶
The public page /video plays the LLARS demo video /videos/llars_demo.mp4.
The file is about 88 MB and is deliberately kept out of the repository and out
of the frontend image (git- and dockerignored). nginx serves it straight from a
directory on the host. On a new host, and whenever /videos/llars_demo.mp4
returns 404, it has to be placed there once by hand.
How to recognise the problem
/video shows the notice "The video can't be loaded right now" with the
YouTube backup, curl -I …/videos/llars_demo.mp4 returns 404 Not Found
(nginx error page, text/html, 146 bytes), and the smoke test reports
WARNING: Demo-Video fehlt auf dem Host.
Where the file lives¶
| What | Value |
|---|---|
| Host path (default) | /var/llars-static/videos/llars_demo.mp4 |
| Variable | LLARS_STATIC_DIR in /var/llars/.env (default /var/llars-static) |
| Path inside the nginx container | /srv/llars-static/videos/ (read-only) |
| Mount | docker-compose.prod.yml (production) and docker-compose.staging.yml (staging :55080) |
| nginx | location /videos/ in nginx.prod.conf (port 80 and 443) and nginx.prod-no-ssl.conf |
| Source | llars-frontend/public/videos/llars_demo.mp4 on a developer machine |
Why outside the checkout¶
Until 2 October 2026 docker-compose.prod.yml mounted the video from
./static/videos, i.e. /var/llars/static/videos inside the checkout. Every
deploy cleans the checkout with git reset --hard and git clean -fd, and
git clean removes everything untracked that is not ignored. Because
deploy_bluegreen.sh rewrites the tracked docker/nginx/active_upstream.conf on
every deploy, the cleanup ran practically every time. On production the
directory was therefore empty, stamped with the time of the nightly deploy
(02:22).
The default now lives under /var/llars-static, out of reach of any
git clean (even with -x) and of a fresh checkout. /static/ is also listed
in .gitignore, and the scripts exclude static/ from git clean, so the old
path is no longer emptied either.
Blue-green¶
Blue and green share one checkout under /var/llars; the colours only differ
in container names and compose projects. The nginx container
(llars_nginx_service) belongs to neither colour. So there is exactly one video
directory, and switching colours does not change it.
Placing the file¶
/var/llars-static sits under /var and needs root rights the first time it is
created. Without sudo this works through a short-lived container, the same way
deploy_bluegreen.sh runs its chown (Docker creates the missing host
directory for -v itself). From a developer machine (SSH alias llars for
production, llars-dev for kia-dev; both need the VPN):
scp llars-frontend/public/videos/llars_demo.mp4 llars:/tmp/llars_demo.mp4
ssh llars 'docker run --rm \
-v /tmp/llars_demo.mp4:/src/llars_demo.mp4:ro \
-v /var/llars-static:/dst \
alpine:3.19 sh -c "mkdir -p /dst/videos \
&& cp /src/llars_demo.mp4 /dst/videos/llars_demo.mp4 \
&& chmod 755 /dst /dst/videos && chmod 644 /dst/videos/llars_demo.mp4" \
&& rm -f /tmp/llars_demo.mp4'
With sudo on the host it works directly:
sudo install -D -m 0644 /tmp/llars_demo.mp4 /var/llars-static/videos/llars_demo.mp4
sudo chmod 755 /var/llars-static /var/llars-static/videos
Permissions matter: the nginx worker runs as user nginx inside the container
and needs read access to the file (644) and the directories (755).
Once, after the switch of 2 October 2026
If the video was previously placed in /var/llars/static/videos/, it has to
be placed in /var/llars-static/videos/ once. Ideally do this before
the deploy that brings the switch, so there is no gap. After the deploy,
check that nginx mounts the new path:
docker inspect llars_nginx_service \
--format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}' | grep videos
If it still shows /var/llars/static/videos, recreate the nginx container
(command below). The old copy under /var/llars/static/ can go afterwards.
Optional: faster start in the browser
In the current file the moov box (metadata) sits at the end. Browsers
fetch it with a range request, which works but takes time on slow
connections. ffmpeg -i llars_demo.mp4 -c copy -movflags +faststart llars_demo_fast.mp4
moves the metadata to the front without re-encoding.
Checking¶
# On the host (emulates the FH gateway, which forwards over HTTP to port 80)
curl -sI -H "X-Forwarded-Proto: https" http://localhost/videos/llars_demo.mp4
# From outside
curl -sI https://llars.e-beratungsinstitut.de/videos/llars_demo.mp4
# Range streaming (seeking, Safari/iOS): expects 206 Partial Content
curl -s -o /dev/null -r 0-0 -w '%{http_code} %{content_type}\n' \
https://llars.e-beratungsinstitut.de/videos/llars_demo.mp4
Expected: 200 OK (or 206 for the range request), Content-Type: video/mp4
and Accept-Ranges: bytes.
The same check runs in every smoke test (scripts/ci/smoke_test_demo_video.sh,
called from smoke_test.sh). If the file is missing it prints a warning with
the target path and keeps the job green, because a rollback would not bring the
file back. SMOKE_STRICT_VIDEO=1 turns it into a hard failure:
The file is on the host, but nginx still returns 404¶
There are two possible causes. Either the running container still mounts the old path (see the box above), or the directory was deleted while nginx was running, so the bind mount still points to the deleted directory. Either way the container does not see the file:
If it is missing there, recreate the nginx container with the same command
deploy_bluegreen.sh uses (it mounts the current active_upstream.conf, so the
active colour stays):
cd /var/llars
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps --force-recreate nginx-service
Staging (:55080) picks up the mount with the next deploy_bluegreen.sh deploy.
Replacing the file¶
Put a new version in place under the same name. nginx serves the video with
expires 7d; browsers that have already loaded it may show the old version from
their cache for up to seven days.