Skip to content

v1.22.0

Latest

Choose a tag to compare

@ravirajsinh45 ravirajsinh45 released this 29 Oct 12:44
· 13 commits to main since this release
a54e6ef

Upgrade notes

  • The installer leaves existing installs alone. Servers set up by hand (a clone built from source, or the published-images compose files) are unaffected and keep working exactly as before; the manual paths stay fully supported. The installer refuses a server that already runs FreeFrame the manual way: it changes nothing and says so. It is for new servers. (#588)
  • The GPU compose file changes nothing unless you use it. docker-compose.gpu.yml is opt-in like the other new files: without -f docker-compose.gpu.yml (or, with the installer, FREEFRAME_GPU=nvidia), the worker runs on the CPU exactly as before, and docker-compose.prod.yml is unchanged. Servers set up by the installer before this change stay on the CPU through freeframe update; turn the GPU on with sudo freeframe configure. (#588)
  • Nothing changes for existing deployments. The new docker-compose.images.yml and docker-compose.storage.yml are opt-in: docker-compose.prod.yml behaves as before (only comment lines change in it, and the web upload-concurrency build arg moves to a runtime setting), including git pull && docker compose --env-file .env.prod -f docker-compose.prod.yml up -d --build. The commented-out Silo block in it stays where it is, so a clone that uncommented it gets no merge conflict. (#587)
  • HTTPS is now a separate compose file, and nothing changes unless you add it. If you set DOMAIN and ACME_EMAIL expecting Let's Encrypt certificates, they were never requested; add -f docker-compose.https.yml after -f docker-compose.prod.yml to get them (see "SSL / TLS Setup" in docs/deployment.md). It needs DOMAIN and ACME_EMAIL set, DNS pointing at the host, and ports 80 and 443 open, and it redirects plain HTTP to HTTPS. Also set FRONTEND_URL=https://<DOMAIN>. With self-hosted storage, serve the storage endpoint (S3_PUBLIC_ENDPOINT) over HTTPS too: the bucket CORS follows FRONTEND_URL, and browsers block http:// storage from an https:// page, so uploads and playback fail otherwise. If you do not add the file, docker-compose.prod.yml behaves exactly as before, including behind Cloudflare in "Full" mode or another proxy. (#583)
  • The upload concurrency is read when the web container starts, not when the image is built. Set UPLOAD_CONCURRENCY in .env.prod and recreate web with docker compose ... up -d web, giving it the same --env-file and every -f file you start FreeFrame with (-f docker-compose.https.yml too if you use it); no rebuild is needed, and docker compose restart does not re-read .env.prod. Your existing NEXT_PUBLIC_UPLOAD_CONCURRENCY line keeps working and gives the same value as before, because UPLOAD_CONCURRENCY is used when set and the older name otherwise. Installs that serve FreeFrame under a sub-path keep building the web image from source exactly as they do now (docker compose --env-file .env.prod -f docker-compose.prod.yml up -d --build), with no new step. A web image built for the domain root now refuses to start when NEXT_PUBLIC_BASE_PATH names a sub-path, with a message saying to build the web image yourself, instead of starting and serving broken links. That includes a web container whose .env.prod sets NEXT_PUBLIC_BASE_PATH while its image was built without it (before, it started at the root): build with --env-file .env.prod, as the documented commands do. (#584)

Added

  • Prebuilt multi-arch images on GHCR for every release. Publishing a release now builds ghcr.io/techiebutler/freeframe-api and ghcr.io/techiebutler/freeframe-web for linux/amd64 and linux/arm64, each on a native runner, and tags them X.Y.Z, vX.Y.Z, X.Y (not for pre-releases, and not when a newer patch of that line exists) and latest (only for GitHub's latest release). Each image carries OCI labels, build provenance and an SBOM, plus a GitHub artifact attestation you can check with gh attestation verify oci://ghcr.io/techiebutler/freeframe-api:vX.Y.Z --repo Techiebutler/freeframe. The latest branch now moves after both images are published rather than the moment the release is, and only for GitHub's latest release, so a patch for an older line published with gh release create --latest=false no longer moves it backwards, and a failed build leaves the branch and the image where they were. The Publish images workflow can also be run by hand for an existing tag, to publish or rebuild its images; a rebuild re-points X.Y, latest and stable only where they still pointed at that version's previous image. (#585)
  • Run FreeFrame from published images, with no clone and no build. The new opt-in docker-compose.images.yml names the ghcr.io/techiebutler/freeframe-api and ghcr.io/techiebutler/freeframe-web images, tagged by version, so a server needs only the compose files and .env.prod: download docker-compose.prod.yml, docker-compose.images.yml and .env.example from the latest ref (the docs switch to stable once the first release with these files has been promoted there), fill in .env.prod and run docker compose --env-file .env.prod -f docker-compose.prod.yml -f docker-compose.images.yml up -d. The file defaults to the release it belongs to, so the compose files and the images move together; set FREEFRAME_VERSION in .env.prod to pin another version, for example to roll back. This works from v1.22.0, the first release that has these compose files. Without the file, docker-compose.prod.yml builds from source as before, and that stays fully supported. (#587)
  • Video processing on an NVIDIA GPU, with the published images. The new opt-in docker-compose.gpu.yml gives the transcoding worker one NVIDIA GPU (with the video capability NVENC needs) and prefers NVENC, keeping the automatic fallback to the CPU. It needs the host's NVIDIA driver and the NVIDIA Container Toolkit; see "Hardware-Accelerated Transcoding" in docs/deployment.md for the steps by hand. The installer checks the server for a GPU and says what it found (for example GPU: NVIDIA GeForce RTX 5070 Ti (driver 610.47), or GPU: none found), and whether video will be processed on the GPU or the CPU. An NVIDIA GPU that Docker can already use is used; for one it cannot use yet, the installer asks Use the NVIDIA GPU for video processing? [Y/n] and, after the install is confirmed, sets up nvidia-container-toolkit from NVIDIA's apt repository and restarts Docker (unattended runs do this only with --gpu nvidia). It never installs drivers: a card without a working driver gets a suggestion, and the install goes on on the CPU, as it does if the toolkit cannot be set up. Intel and AMD GPUs are named and explained (the published images use NVIDIA only). WSL2 works. sudo freeframe configure turns the GPU on or off later (--gpu nvidia or --gpu off), and sudo freeframe status says where video is processed and checks that the worker sees the GPU. After every start with the GPU (install, update, rollback, restore, restart, configure), the worker is asked whether it sees the GPU and is started again once if it does not; if it still does not, the command finishes anyway and says video is processed on the CPU for now, with what to do. A systemctl daemon-reload takes the GPU from running containers when Docker uses the systemd cgroup driver, so the installer reloads systemd for the backup timer before it starts FreeFrame, and update and rollback reload it only when the backup units changed. (#588)
  • Bundled storage in its own compose file. The new opt-in docker-compose.storage.yml runs Silo (the same images as the commented-out block in docker-compose.prod.yml) and creates the bucket, so it survives re-downloading the compose files. STORAGE_ROUTED=true with STORAGE_DOMAIN makes Traefik serve it over HTTPS (with docker-compose.https.yml); STORAGE_PORT_BIND sets where its S3 port is published on the host (default 127.0.0.1:9000, the console is never published). (#587)
  • Install FreeFrame on a fresh server with one command. On Ubuntu 22.04, 24.04 or 26.04 or Debian 12 or 13 (amd64 or arm64), sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/Techiebutler/freeframe/latest/install.sh)" opens with a short welcome and asks four or five short questions as numbered steps and menus (web address with the certificate email for HTTPS, storage, email, admin account, and video processing on a server with an NVIDIA GPU that Docker cannot use yet), installs Docker if it is missing, starts FreeFrame from the published images, gets a Let's Encrypt certificate for your own domain or for a free <ip>.sslip.io address, creates the admin account and turns on nightly database backups (03:00, newest 7 kept). Every question can instead be answered with a FREEFRAME_* environment variable or an option for cloud-init and Ansible. The same download installs a freeframe command at /usr/local/bin/freeframe with status, update (backs up first), rollback, configure, backup, restore, logs, restart and uninstall. The command works from v1.22.0, the first release that has the installer; the documented command names the latest ref for now and moves to stable once a release with the installer is promoted there. See "Install on a fresh server" in docs/deployment.md. The installer is for Linux servers only; on a Mac or Windows it stops and points at the manual compose setup. On Ubuntu 26.04, whose sudo is sudo-rs, a piped curl ... | sudo bash cannot read your answers, so the installer stops before downloading anything and shows the bash -c command; a piped command still works for unattended installs with --yes. (#588)
  • Remove FreeFrame with sudo freeframe uninstall. It saves a final backup of the database first and moves it, with the earlier backups and a copy of .env.prod, to /root/freeframe-removed-<date>-<time>/ (if that backup fails, nothing is removed), then removes FreeFrame's containers and network, the nightly backup timer, /opt/freeframe and the freeframe command. The data stays in its Docker volumes, so putting the saved .env.prod back and running the installer brings FreeFrame back with it (with --yes it needs no admin answers when the kept database has its admin). --delete-data deletes the volumes too, after you type delete (unattended: --yes --delete-data --confirm-delete-data); --skip-backup makes no final backup. Docker and its images, NVIDIA's toolkit, the apt sources, swap and firewall rules are left alone, with a note on how to remove each. --dry-run shows exactly what it would remove and keep, and changes nothing. It refuses a FreeFrame set up by hand. See "Removing FreeFrame" in docs/deployment.md. (#588)

Changed

  • The web app's upload concurrency is a runtime setting, UPLOAD_CONCURRENCY. It used to be baked into the web image at build time as NEXT_PUBLIC_UPLOAD_CONCURRENCY, so changing it meant rebuilding. The pages are now rendered per request and the server hands the value to the browser, so recreating the container is enough. NEXT_PUBLIC_UPLOAD_CONCURRENCY is still read when UPLOAD_CONCURRENCY is unset, and docker-compose.prod.yml no longer passes it as a build argument. The production web and API image builds also now ignore local files such as node_modules and .next, which makes a build from a development checkout much faster. (#584)
  • Promoting a release to stable also moves the stable image tag, to the exact image the release's own tag points at (a retag, never a rebuild). Promotion now refuses, before the branch moves, a tag that has no published images. To roll back to a release from before images were published (older than v1.21.0), run it with branch_only: true, which moves only the branch; it is refused for a tag that has published images. (#585)

Fixed

  • HTTPS certificates were never requested. The production compose routes by path only, and Traefik takes the domain to request a certificate for from a host rule, so it logged No domain parsed in provider ACME and requested nothing, whatever DOMAIN and ACME_EMAIL were set to. The new opt-in docker-compose.https.yml adds host-based routers, so Traefik asks Let's Encrypt for DOMAIN, and redirects plain HTTP to HTTPS. The existing routers keep their rules. (#583)
  • Copy buttons work when FreeFrame is served over plain HTTP. The share links table, a comment's Copy Link, the share link detail page and the collection share dialog called navigator.clipboard directly, which does not exist outside a secure context (for example http://192.168.1.20:3000), so the table and comment buttons threw and the others did nothing. They now use the shared clipboard helper, which falls back to the browser's copy command, and the copied state shows only when the copy worked. (#586)
  • A fresh production install now redirects to /setup. The web app's setup check called the API at the browser's address, /api, which a server cannot resolve, so it failed quietly and the redirect to the setup page never happened in production. It now uses API_INTERNAL_URL, the address the branding fetch already used. (#584)