first version

This commit is contained in:
cpu
2024-01-31 14:28:36 +01:00
parent ffa90bce8b
commit 3f15c9e579
11 changed files with 358 additions and 1 deletions

View File

@@ -1,2 +1,12 @@
# ansible-role-immich # PeerTube Ansible role
This is an [Ansible](https://www.ansible.com/) role which installs [Immich](https://immich.app/) to run as a [Docker](https://www.docker.com/) container wrapped in a systemd service.
This role *implicitly* depends on:
- [`com.devture.ansible.role.playbook_help`](https://github.com/devture/com.devture.ansible.role.playbook_help)
- [`com.devture.ansible.role.systemd_docker_base`](https://github.com/devture/com.devture.ansible.role.systemd_docker_base)
Check [defaults/main.yml](defaults/main.yml) for the full list of supported options.
For an Ansible playbook which integrates this role and makes it easier to use, see the [mash-playbook](https://github.com/mother-of-all-self-hosting/mash-playbook).

90
defaults/main.yml Normal file
View File

@@ -0,0 +1,90 @@
---
# Project source code URL: https://github.com/imagegenius/docker-immich
immich_enabled: true
immich_identifier: immich
# latest - Latest Immich release with an Ubuntu base.
# noml - Latest Immich release with an Ubuntu base. Machine-learning is completely removed, making it still compatible with hardware accelaration.
# alpine - Latest Immich release with an Alpine base. Machine-learning is completely removed, making it a very lightweight image (can have issues with RAW images).
immich_version: 'v1.93.3-ig193'
immich_distro_variant: noml
immich_uid: ''
immich_gid: ''
# The hostname at which Immich is served.
immich_hostname: ''
# The path at which Immich is exposed.
# This value must either be `/` or not end with a slash (e.g. `/immich`).
immich_path_prefix: /
immich_base_path: "{{ immich_playbook_base_path }}/immich"
immich_config_dir_path: "{{ immich_base_path }}/config"
immich_photos_dir_path: "{{ immich_base_path }}/photos"
immich_import_dir_path: "{{ immich_base_path }}/import"
immich_systemd_required_services_list: "{{ immich_systemd_required_services_list_default + immich_systemd_required_services_list_auto + immich_systemd_required_services_list_custom }}"
immich_systemd_required_services_list_default: ['docker.service']
immich_systemd_required_services_list_auto: []
immich_systemd_required_services_list_custom: []
immich_config_database_hostname: ''
immich_config_database_port: 5432
immich_config_database_name: immich
immich_config_database_username: ''
immich_config_database_password: ''
immich_config_machine_learning_workers: ''
immich_config_machine_learning_workers_timeout: 120
# Controls the IMMICH_REDIS_HOSTNAME environment variable
immich_config_redis_hostname: ''
# Controls the IMMICH_LOG_LEVEL environment variable.
# Valid values: debug, info, warn, error
immich_config_log_level: info
immich_container_image: "{{ immich_container_image_registry_prefix }}imagegenius/immich:{{ immich_container_image_tag }}"
immich_container_image_registry_prefix: ghcr.io/
immich_container_image_tag: "{{ immich_distro_variant }}-{{ immich_version }}"
immich_container_image_force_pull: "{{ immich_container_image_registry_prefix }}imagegenius/immich:latest"
# The base container network. It will be auto-created by this role if it doesn't exist already.
immich_container_network: "{{ immich_identifier }}"
# A list of additional container networks that the container would be connected to.
# The playbook does not create these networks, so make sure they already exist.
# Use this to expose the container to another reverse proxy, which runs in a different container network.
immich_container_additional_networks: "{{ immich_container_additional_networks_auto + immich_container_additional_networks_custom }}"
immich_container_additional_networks_auto: []
immich_container_additional_networks_custom: []
# immich_container_labels_traefik_enabled controls whether labels to assist a Traefik reverse-proxy will be attached to the container.
# See `roles/immich/immich/templates/labels.j2` for details.
#
# To inject your own other container labels, see `immich_container_labels_additional_labels`.
immich_container_labels_traefik_enabled: true
immich_container_labels_traefik_docker_network: ''
immich_container_labels_traefik_hostname: "{{ immich_hostname }}"
# The path prefix must either be `/` or not end with a slash (e.g. `/immich`).
immich_container_labels_traefik_path_prefix: "{{ immich_path_prefix }}"
immich_container_labels_traefik_rule: "Host(`{{ immich_container_labels_traefik_hostname }}`){% if immich_container_labels_traefik_path_prefix != '/' %} && PathPrefix(`{{ immich_container_labels_traefik_path_prefix }}`){% endif %}"
immich_container_labels_traefik_priority: 0
immich_container_labels_traefik_entrypoints: web-secure
immich_container_labels_traefik_tls: "{{ immich_container_labels_traefik_entrypoints != 'web' }}"
immich_container_labels_traefik_tls_certResolver: default # noqa var-naming
# A list of extra arguments to pass to the container
immich_container_extra_arguments: []
# immich_container_additional_environment_variables contains a multiline string with additional environment variables to pass to the container.
#
# Example:
# immich_container_additional_environment_variables: |
# VAR=1
# ANOTHER=value
immich_container_additional_environment_variables: ''

6
justfile Normal file
View File

@@ -0,0 +1,6 @@
# show help by default
default:
@just --list --justfile {{ justfile() }}
lint:
ansible-lint .

21
meta/main.yml Normal file
View File

@@ -0,0 +1,21 @@
galaxy_info:
author: virt
company: virt
role_name: immich
namespace: mash
description: immich
platforms:
- name: Debian
versions:
- all
- name: Ubuntu
versions:
- all
- name: Archlinux
versions:
- all
- name: EL
versions:
- 7
license: GPL-3.0-or-later
min_ansible_version: '2.1'

43
tasks/install.yml Normal file
View File

@@ -0,0 +1,43 @@
---
- name: Ensure Immich paths exist
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: "0750"
owner: "{{ immich_uid }}"
group: "{{ immich_gid }}"
with_items:
- "{{ immich_base_path }}"
- "{{ immich_config_dir_path }}"
- "{{ immich_photos_dir_path }}"
- "{{ immich_import_dir_path }}"
- name: Ensure Immich support files created
ansible.builtin.template:
src: "{{ role_path }}/templates/{{ item }}.j2"
dest: "{{ immich_base_path }}/{{ item }}"
owner: "{{ immich_uid }}"
group: "{{ immich_gid }}"
mode: 0640
with_items:
- env
- labels
- name: Ensure Immich container network is created
community.general.docker_network:
name: "{{ immich_container_network }}"
driver: bridge
- name: Ensure Immich container image is pulled
community.docker.docker_image:
name: "{{ immich_container_image }}"
source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}"
force_source: "{{ immich_container_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}"
force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else immich_container_image_force_pull }}"
- name: Ensure Immich systemd service installed
ansible.builtin.template:
src: "{{ role_path }}/templates/immich.service.j2"
dest: "{{ devture_systemd_docker_base_systemd_path }}/{{ immich_identifier }}.service"
mode: 0644

20
tasks/main.yml Normal file
View File

@@ -0,0 +1,20 @@
---
- block:
- when: immich_enabled | bool
ansible.builtin.include_tasks: "{{ role_path }}/tasks/validate_config.yml"
- when: immich_enabled | bool
ansible.builtin.include_tasks: "{{ role_path }}/tasks/install.yml"
tags:
- setup-all
- setup-immich
- install-all
- install-immich
- block:
- when: not immich_enabled | bool
ansible.builtin.include_tasks: "{{ role_path }}/tasks/uninstall.yml"
tags:
- setup-all
- setup-immich

25
tasks/uninstall.yml Normal file
View File

@@ -0,0 +1,25 @@
---
- name: Check existence of Immich systemd service
ansible.builtin.stat:
path: "{{ devture_systemd_docker_base_systemd_path }}/{{ immich_identifier }}.service"
register: immich_service_stat
- when: immich_service_stat.stat.exists | bool
block:
- name: Ensure Immich systemd service is stopped
ansible.builtin.service:
name: "{{ immich_identifier }}"
state: stopped
enabled: false
daemon_reload: true
- name: Ensure Immich systemd service does not exists
ansible.builtin.file:
path: "{{ devture_systemd_docker_base_systemd_path }}/{{ immich_identifier }}.service"
state: absent
- name: Ensure Immich path doesn't exist
ansible.builtin.file:
path: "{{ immich_base_path }}"
state: absent

38
tasks/validate_config.yml Normal file
View File

@@ -0,0 +1,38 @@
---
- name: Fail if required Immich settings not defined
ansible.builtin.fail:
msg: >-
You need to define a required configuration setting (`{{ item }}`) for using this role.
when: "vars[item] | string == ''"
with_items:
- immich_identifier
- immich_uid
- immich_gid
- immich_container_network
- immich_hostname
- immich_path_prefix
- immich_config_database_hostname
- immich_config_database_username
- immich_config_database_password
- immich_config_redis_hostname
- when: immich_container_labels_traefik_enabled | bool
block:
- name: Fail if required Immich Traefik settings not defined
ansible.builtin.fail:
msg: >-
You need to define a required configuration setting (`{{ item }}`).
when: "vars[item] == ''"
with_items:
- immich_container_labels_traefik_hostname
- immich_container_labels_traefik_path_prefix
# We ensure it doesn't end with a slash, because we handle both (slash and no-slash).
# Knowing that `immich_container_labels_traefik_path_prefix` does not end with a slash
# ensures we know how to set these routes up without having to do "does it end with a slash" checks elsewhere.
- name: Fail if immich_container_labels_traefik_path_prefix ends with a slash
ansible.builtin.fail:
msg: >-
immich_container_labels_traefik_path_prefix (`{{ immich_container_labels_traefik_path_prefix }}`) must either be `/` or not end with a slash (e.g. `/immich`).
when: "immich_container_labels_traefik_path_prefix != '/' and immich_container_labels_traefik_path_prefix[-1] == '/'"

19
templates/env.j2 Normal file
View File

@@ -0,0 +1,19 @@
DB_USERNAME={{ immich_config_database_username }}
DB_PASSWORD={{ immich_config_database_password }}
DB_DATABASE_NAME={{ immich_config_database_name }}
IMMICH_DB_SSL=false
DB_HOSTNAME={{ immich_config_database_hostname }}
DB_PORT={{ immich_config_database_port }}
REDIS_HOSTNAME={{ immich_config_redis_hostname }}
PUID={{ immich_uid }}
PGID={{ immich_gid }}
TZ=Europe/Bratislava
MACHINE_LEARNING_WORKERS={{ immich_config_machine_learning_workers }}
MACHINE_LEARNING_WORKER_TIMEOUT={{ immich_config_machine_learning_workers_timeout }}
IMMICH_LOG_LEVEL={{ immich_config_log_level }}
{{ immich_container_additional_environment_variables }}

View File

@@ -0,0 +1,47 @@
[Unit]
Description=Immich ({{ immich_identifier }})
{% for service in immich_systemd_required_services_list %}
Requires={{ service }}
After={{ service }}
{% endfor %}
DefaultDependencies=no
[Service]
Type=simple
Environment="HOME={{ devture_systemd_docker_base_systemd_unit_home_path }}"
ExecStartPre=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} stop --time={{ devture_systemd_docker_base_container_stop_grace_time_seconds }} {{ immich_identifier }} 2>/dev/null'
ExecStartPre=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} rm {{ immich_identifier }} 2>/dev/null'
ExecStartPre={{ devture_systemd_docker_base_host_command_docker }} create \
--rm \
--name={{ immich_identifier }} \
--log-driver=none \
--network={{ immich_container_network }} \
--user={{ immich_uid }}:{{ immich_gid }} \
--cap-drop=ALL \
--read-only \
--env-file={{ immich_base_path }}/env \
--label-file={{ immich_base_path }}/labels \
--mount type=bind,src={{ immich_config_dir_path }},dst=/config \
--mount type=bind,src={{ immich_photos_dir_path }},dst=/photos \
--mount type=bind,src={{ immich_import_dir_path }},dst=/import,ro \
--tmpfs=/tmp:rw,noexec,nosuid,size=512m \
{% for arg in immich_container_extra_arguments %}
{{ arg }} \
{% endfor %}
{{ immich_container_image }}
{% for network in immich_container_additional_networks %}
ExecStartPre={{ devture_systemd_docker_base_host_command_docker }} network connect {{ network }} {{ immich_identifier }}
{% endfor %}
ExecStart={{ devture_systemd_docker_base_host_command_docker }} start --attach {{ immich_identifier }}
ExecStop=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} stop --time={{ devture_systemd_docker_base_container_stop_grace_time_seconds }} {{ immich_identifier }} 2>/dev/null'
ExecStop=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} rm {{ immich_identifier }} 2>/dev/null'
Restart=always
RestartSec=30
SyslogIdentifier={{ immich_identifier }}
[Install]
WantedBy=multi-user.target

38
templates/labels.j2 Normal file
View File

@@ -0,0 +1,38 @@
{% if immich_container_labels_traefik_enabled %}
traefik.enable=true
{% if immich_container_labels_traefik_docker_network %}
traefik.docker.network={{ immich_container_labels_traefik_docker_network }}
{% endif %}
{% set middlewares = [] %}
{% if immich_container_labels_traefik_path_prefix != '/' %}
traefik.http.middlewares.{{ immich_identifier }}-slashless-redirect.redirectregex.regex=^({{ immich_container_labels_traefik_path_prefix | quote }})$
traefik.http.middlewares.{{ immich_identifier }}-slashless-redirect.redirectregex.replacement=${1}/
{% set middlewares = middlewares + [immich_identifier + '-slashless-redirect'] %}
{% endif %}
{% if immich_container_labels_traefik_path_prefix != '/' %}
traefik.http.middlewares.{{ immich_identifier }}-strip-prefix.stripprefix.prefixes={{ immich_container_labels_traefik_path_prefix }}
{% set middlewares = middlewares + [immich_identifier + '-strip-prefix'] %}
{% endif %}
traefik.http.routers.{{ immich_identifier }}.rule={{ immich_container_labels_traefik_rule }}
{% if immich_container_labels_traefik_priority | int > 0 %}
traefik.http.routers.{{ immich_identifier }}.priority={{ immich_container_labels_traefik_priority }}
{% endif %}
traefik.http.routers.{{ immich_identifier }}.service={{ immich_identifier }}
{% if middlewares | length > 0 %}
traefik.http.routers.{{ immich_identifier }}.middlewares={{ middlewares | join(',') }}
{% endif %}
traefik.http.routers.{{ immich_identifier }}.entrypoints={{ immich_container_labels_traefik_entrypoints }}
traefik.http.routers.{{ immich_identifier }}.tls={{ immich_container_labels_traefik_tls | to_json }}
{% if immich_container_labels_traefik_tls %}
traefik.http.routers.{{ immich_identifier }}.tls.certResolver={{ immich_container_labels_traefik_tls_certResolver }}
{% endif %}
traefik.http.services.{{ immich_identifier }}.loadbalancer.server.port=8080
{% endif %}
{{ immich_container_labels_additional_labels }}