HomeLab services - Immich and Jellyfin

HomeLab services - Immich and Jellyfin

Table of contents

In this article, we will be exploring the following services

I use these two on a daily basis. You can explore other self-hosted services in https://selfh.st/

Since we have already discussed reverse proxy, setting up VPN and other topics, this blog will be quite easy to pick up because we are just adding new services.

Immich

Immich is an image backup service. It looks almost like a clone of Google Photos, and I feel like it's too good to be true. You basically host your own Google Photos, download a mobile app available on both Android and iOS, and now your personal computer is the cloud. You can easily back up all your photos to your computer while also being able to share these photos, create albums, group by faces, and much more.

To set up Immich, it's as easy as it gets. But first, let's create a docker network so the nginx and immich can remain in the same docker network

docker network create homelab_network

Now we use this network in both the nginx and immich.

Nginx docker-compose.yml

version: '2.2' 
services:
  nginxproxymanager:
    image: 'jc21/nginx-proxy-manager:latest' 
    container_name: nginxproxymanager
    restart: unless-stopped 
    ports:
      - '80:80'
      - '81:81'
      - '443:443' 
    volumes:
      - ./nginx/data:/data
      - ./nginx/letsencrypt:/etc/letsencrypt 

networks:
  default:
    name: homelab_network
    external: True

Immich docker-compose.yml

name: immich

services:
  immich-server:
    container_name: immich_server
    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}    
    volumes:
      - ${UPLOAD_LOCATION}:/usr/src/app/upload
      - /etc/localtime:/etc/localtime:ro
    env_file:
      - .env
    ports:
      - 2283:3001
    depends_on:
      - redis
      - database
    restart: always

  immich-machine-learning:
    container_name: immich_machine_learning
    image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
    volumes:
      - model-cache:/cache
    env_file:
      - .env
    restart: always

  redis:
    container_name: immich_redis
    image: docker.io/redis:6.2-alpine@sha256:d6c2911ac51b289db208767581a5d154544f2b2fe4914ea5056443f62dc6e900
    healthcheck:
      test: redis-cli ping || exit 1
    restart: always

  database:
    container_name: immich_postgres
    image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_DB: ${DB_DATABASE_NAME}
      POSTGRES_INITDB_ARGS: '--data-checksums'
    volumes:
      - ${DB_DATA_LOCATION}:/var/lib/postgresql/data
    healthcheck:
      test: pg_isready --dbname='${DB_DATABASE_NAME}' || exit 1; Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
      interval: 5m
      start_interval: 30s
      start_period: 5m
    command: ["postgres", "-c" ,"shared_preload_libraries=vectors.so", "-c", 'search_path="$$user", public, vectors', "-c", "logging_collector=on", "-c", "max_wal_size=2GB", "-c", "shared_buffers=512MB", "-c", "wal_compression=on"]
    restart: always

volumes:
  model-cache:

networks:
  default:
    name: homelab_network
    external: True

For immich to run, we need to create a .env file to set the following variables

# You can find documentation for all the supported env variables at https://immich.app/docs/install/environment-variables

# The location where your uploaded files are stored
UPLOAD_LOCATION="/mnt/petrificus totalus/immich/library"
# The Immich version to use. You can pin this to a specific version like "v1.71.0"
IMMICH_VERSION=release

# Connection secret for postgres. You should change it to a random password
DB_PASSWORD=

# The values below this line do not need to be changed
###################################################################################
DB_HOSTNAME=immich_postgres
DB_USERNAME=postgres
DB_DATABASE_NAME=immich
DB_DATA_LOCATION=./postgres

REDIS_HOSTNAME=immich_redis

After starting the containers with docker-compose up, we can easily access the immich service by going to localhost:2283

After creating an administrator account, you can download the Immich mobile app. The app will ask you to enter your server location. If the server is accessible, you will be asked to log in. After authentication, you will have a Google Photos-like experience with your own storage, without Google pushing you to buy their #cheap storage plans.

I won't go into how to use Immich because it's similar to Google Photos, and the Immich documentation is quite user-friendly.

Now we need to add a proxy to access Immich using a domain name. To do this, follow the steps in the previous article. Create a subdomain of your choice, for example, immich.example.com.

The proxy configuration will look like this. We use "immich-server" as the hostname because both Nginx and Immich are on the same network. With Docker, the container's hostname will be the service name defined in the docker-compose file.

This allows Nginx to easily send requests to immich-server:3001 when the request comes from immich.donthavemoney.com.

Jellyfin

Jellyfin is a media hosting service that continues to impress me with its capabilities. Let's get straight to setting it up.

The docker-compose file needed to set up Jellyfin is straightforward, so we will add its services directly into the nginx compose file.

version: '2.2' 
services:
  nginxproxymanager:
    image: 'jc21/nginx-proxy-manager:latest' 
    container_name: nginxproxymanager
    restart: unless-stopped 
    ports:
      - '80:80'
      - '81:81'
      - '443:443' 
    volumes:
      - ./nginx/data:/data
      - ./nginx/letsencrypt:/etc/letsencrypt 


  jellyfin:
    image: lscr.io/linuxserver/jellyfin:latest
    container_name: jellyfin 
    ports:
      - 8096:8096
      - 8920:8920
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Berlin 
    volumes:
      - ./jellyfin/config:/config
      - ./jellyfin/tvshows:/data/tvshows
      - "/path/to/your/Anime:/data/anime"
      - "/path/to/your/movies:/data/movies" 
    restart: unless-stopped

networks:
  default:
    name: homelab_network
    external: True

So, all you need to do is add the Jellyfin service to the existing Nginx service. Another important task is creating a bind map of your local media directories to the /data directory of the container. Since Jellyfin is inside the same Docker Compose file, we can create reverse proxies similarly.

This is what Jellyfin looks like. Some of the features I absolutely love about Jellyfin are:

  • Sync play. You can add guest users and sync play between multiple devices and users

  • All the watched media and last viewed times are saved.

  • We can control user access and multiple users can consume the same library

  • So basically my movie and anime collection can be shared and used as my own personal Netflix

  • A smooth mobile app

This concludes my blog series on creating your own home lab. While it may sound fancy and complicated, a home lab is indeed fancy, but not that complicated. I loved doing all this when I first got into it. I even got my first domain name specifically for this purpose.

From here onwards, it's all about exploring other open-source tools and experimenting using your own computer. For example, you can start by setting up your own GitLab, setting up Pi-hole for network-wide ad blocking., Home automation with Home Assistant or Home bridge and many more. You can even go as far as setting up everything using Kubernetes, which I don't know anything about.

In conclusion, setting up a basic home lab helps you build a solid foundation on Linux, computer networking and some amount of DevOps.