CLI tool
ArnaudCrl/immich-automated-selfie-timelapse avatar
ArnaudCrl/immich-automated-selfie-timelapse

immich-automated-selfie-timelapse: a 0.1.0 manifest shipping a v2.3.0 image

Automated face extraction, resizing and alignment suitable to make a selfie timelapse video.

806 stars22 forksRustMIT

At a glance

What is it?
A Rust container that reads face metadata out of Immich, runs a stack of quality filters over each portrait and compiles an MP4 with FFmpeg. Its source manifest says 0.1.0, its image tags say v2.3.0, and the two documented install paths disagree about which uid writes to your volumes.
Who is it for?
immich-automated-selfie-timelapse suits one person and one subject, run as a batch job on a machine with spare CPU, where the outcome is judged by eye at the end. It does not suit a scheduled service, since the recommended Compose file restarts a container the README calls one shot, and it does not suit a build you need to reproduce offline, since two models are fetched over the network during the image build.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 86 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The manifest has never been bumped past 0.1.0 and four names are in play

Cargo.toml declares `name = "immich-timelapse"` and `version = "0.1.0"` with `edition = "2021"`, an MIT license field, the repository URL, and a single binary target named `immich-timelapse` at `src/main.rs`. GitHub releases for this repository are at v2.3.0, published 2026-07-08, with v2.2.1 on 2026-03-30 and a v2.2.1-pre-release-1 on 2026-03-12. The last push to the default branch was 2026-07-08, hours after that release, so the repository is current and the version field simply was never updated alongside the tags. The naming is just as layered. The repository is immich-automated-selfie-timelapse, the crate is immich-timelapse, the image is arnaudcayrol/immich-selfie-timelapse, and the Compose service and container are both immich-selfie-timelapse. The project author handle is ArnaudCrl while the image namespace is arnaudcayrol, and the project homepage recorded for it is the Docker Hub page rather than the repository.

An ONNX runtime is frozen at a release candidate while the compiler floats

One ML dependency is pinned harder than anything else in the manifest. `ort = "2.0.0-rc.11"` carries no caret and no range, so the ONNX runtime is a single release candidate build with no allowance for a patch. The compiler that has to build against it is not pinned at all. The Dockerfile installs Rust by piping the official script straight into a shell:

bash
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable

`--default-toolchain stable` resolves to whatever stable is on the day the image is built. Every rebuild of this image is therefore a different compiler against the same pre-release runtime. The rest of the dependency list is ordinary and unremarkable: tokio with the full feature set, axum 0.8 with websocket support, tower-http with fs, cors and set-header, reqwest 0.12 with json and stream, image 0.25 with jpeg, png and webp, imageproc 0.25, kamadak-exif 0.5, ndarray 0.16, thiserror, anyhow, tracing-subscriber with env-filter, chrono with serde, directories 5, dotenvy 0.15 and unicode-normalization. The one entry that reaches outside the Rust ecosystem is `dlib-face-recognition = { version = "0.3" }`, which binds to system libraries the image installs separately.

Both landmark models are downloaded at image build time from outside the repository

The runtime stage of the Dockerfile fetches two model files over the network and verifies both with sha256sum before use:

bash
RUN mkdir -p models && \
    curl -fsSL -o models/dmhead_nomask_Nx3x224x224.onnx \
        https://github.com/PINTO0309/DMHead/releases/download/1.1.2/dmhead_nomask_Nx3x224x224.onnx && \
    echo "8dd5643923680b3a8e27507c4fc3d7331e0044a2554169e5658770d5b27fd122  models/dmhead_nomask_Nx3x224x224.onnx" | sha256sum -c && \

The same pattern repeats for a second file, shape_predictor_68_face_landmarks.dat.bz2, pulled from dlib.net and checked against 7d6637b8f34ddb0c1363e09a4628acb34314019ec3566fd66b80c04dda6980f5. The checksums are the right shape, since a build that downloads binaries should pin them. What the checksums do not solve is availability. Both files live outside this repository and outside the crate registry, one behind a third party GitHub release tag and one behind a vendor file host, so the image builds only while both URLs serve those exact bytes. Neither appears in Cargo.lock, because neither is a crate. The head pose filter described as DMHead in the feature list is the first file, and the landmark model is the second.

The docker run alternative drops the user and restart lines Compose carries

The recommended path is a Compose service, and it sets two things the single container command does not:

yaml
services:
  immich-selfie-timelapse:
    image: arnaudcayrol/immich-selfie-timelapse
    container_name: immich-selfie-timelapse
    user: 1000:1000
    ports:
      - "5000:5000"
    environment:
      - IMMICH_API_KEY=abcdefghijklmnopqrstuvwxyz
      - IMMICH_BASE_URL=http://your-immich-host:2283
    volumes:
      - ./config:/app/config
      - ./output:/app/output
    restart: unless-stopped

The Docker Run section gives the image, the port, the two environment variables and the two volume mounts, and stops there. No `user`, no `restart`. That matters because the next section of the documentation asks you to run `chown -R 1000:1000 ./config ./output` and to ensure correct read and write permissions on the config and output folders. Those instructions are written for the Compose path, where the container was told to run as uid 1000. Take the docker run block instead and the chown still applies to your host directories while the uid inside the container is whatever the image declares, which the documentation never states. The two paths are presented as equivalent and are not.

Six filters stand between a photo and a frame, and the author says they leak

The feature list is really a pipeline. Immich's own face recognition metadata locates and crops faces, eye positions align them so the frame does not jitter, and then six independent gates can reject a photo: head pose filtering skips non front facing shots through the DMHead model, blink detection drops closed eyes using Eye Aspect Ratio, blur detection discards soft frames using gradient magnitude analysis with the Sobel operator, brightness filtering removes shots that are too dark or overexposed, face resolution filtering skips faces that are too small, and photo limiting caps the count per day, week or month. Timestamp overlay is optional and video compilation runs the survivors through FFmpeg into an MP4. The documentation is unusually blunt about the result. It says the default settings are quite permissive because every human being is unique, that you should adjust brightness filtering and eye aspect ratio for the person you are processing, that photo filtering is not 100 percent accurate and will continue to improve, and that there are always outliers that pass the quality filters.

Seven Immich scopes, one person, and the key lands in shell history either way

The pipeline never scans images itself. It asks Immich for face metadata and works from what comes back, so the API key needs album.read, asset.download, asset.read, asset.view, face.read, person.read and server.about. The Immich API key is stored as IMMICH_API_KEY and the server as IMMICH_BASE_URL, with the examples using port 2283, and the web interface is served on port 5000. The documentation advises keeping the key in a .env file adjacent to docker-compose.yml rather than in plain text, and `dotenvy = "0.15"` is a dependency, so the binary does read one. The two examples then put the key in plain sight anyway: the Compose sample sets `IMMICH_API_KEY=abcdefghijklmnopqrstuvwxyz` inline in the environment list, and the docker run sample passes `-e IMMICH_API_KEY=your-api-key-here` on the command line, where it persists in shell history and remains visible to anyone who can inspect the container. Volume paths are fixed at /app/config for a persisted config.toml and /app/output for processed images and the compiled video.

Photo limiting changes pacing rather than quality, and one shot plus unless-stopped collide

One filter in that chain works on time instead of pixels. Photo limiting caps photos per day, week or month to avoid over representing busy periods, which means a holiday week with hundreds of frames does not turn into a burst that plays for a minute while a quiet month does not vanish entirely. It is the setting that decides how long the finished video is rather than which faces survive. The deployment guidance pulls the other way. The README calls this a one shot application that does not necessarily need to be deployed on an always on server, and suggests moving the container to a heavier PC for faster generation, which is a batch job description. The recommended Compose file nonetheless carries `restart: unless-stopped`, so the shipped default brings the container back after a host reboot even though the work it does is a single compile. The web interface is the other half of that story: it is where all settings are configured and progress is monitored, and where the Compile video button at the bottom lives.

Issues are welcomed at the author's pace, pull requests are not scheduled

The contribution note is the most unusual paragraph in the documentation and worth reading before you plan any work. It says the project was originally marked open to contributions, that the author no longer has as much time as expected to dedicate to it, that issues being opened is welcome because it lets the author go through them at their own pace, and that for pull requests no reasonable time frame for review can be guaranteed. That is an honest statement rather than a closed door, but it does mean the only commitment on offer is an eventual look at an issue. The licensing is unambiguous in contrast: MIT, with a LICENSE file at the root and the same identifier in Cargo.toml. The repository sits at 806 stars and 22 forks with 13 open issues, and the README closes by thanking the Immich team and contributors, thanking thomaslrg for discussions around the project, and correcting its own star milestone to 600. Last push to the default branch was 2026-07-08.

Editorial conclusion

immich-automated-selfie-timelapse suits one person and one subject, run as a batch job on a machine with spare CPU, where the outcome is judged by eye at the end. It does not suit a scheduled service, since the recommended Compose file restarts a container the README calls one shot, and it does not suit a build you need to reproduce offline, since two models are fetched over the network during the image build. Check four things before you trust an output. Check that your Immich API key carries album.read, asset.download, asset.read, asset.view, face.read, person.read and server.about, because the pipeline reads face metadata rather than scanning images itself. Check the chown on ./config and ./output if you take the docker run path instead of Compose, because that command carries no user setting. Check your brightness and eye aspect ratio thresholds, since the defaults are described as permissive. And plan on reviewing the gallery and pressing Compile video again, because the filtering is documented as never 100 percent accurate. Last push on the default branch was 2026-07-08.

Frequently asked questions

What does immich-automated-selfie-timelapse need from an Immich server?

An Immich API key with album.read, asset.download, asset.read, asset.view, face.read, person.read and server.about enabled, plus IMMICH_BASE_URL pointing at your server, with the examples using port 2283. The project reads Immich's face recognition metadata rather than detecting faces itself.

How do I run immich-automated-selfie-timelapse?

Two paths are given. A Docker Compose service using the arnaudcayrol/immich-selfie-timelapse image, or a docker run command. Both mount ./config to /app/config for a persisted config.toml and ./output to /app/output for processed images and the compiled video, and the web interface is served on port 5000.

Does the immich timelapse container run as a non root user?

The Compose service sets user: 1000:1000 and the documentation asks you to chown -R 1000:1000 ./config ./output. The docker run alternative omits both the user setting and the restart policy, so the two documented paths do not agree on the uid that writes to your volumes.

How accurate is the photo filtering in immich-automated-selfie-timelapse?

The documentation says the filtering is not 100 percent accurate and will continue to improve, and that outliers always pass. The suggested remedy is to go into the gallery view, remove the bad photos, and press the Compile video button at the bottom to recreate the output.

Official sources

  1. ArnaudCrl/immich-automated-selfie-timelapse on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/arnaudcrl-immich-automated-selfie-timelapse.svg)](https://hysenlabs.com/projects/arnaudcrl-immich-automated-selfie-timelapse)