diff --git a/nixos/modules/services/web-apps/immich.md b/nixos/modules/services/web-apps/immich.md new file mode 100644 index 0000000000000..2b9a93776f9e5 --- /dev/null +++ b/nixos/modules/services/web-apps/immich.md @@ -0,0 +1,269 @@ +# Immich {#module-immich} + +*Source:* {file}`modules/services/web-apps/immich.nix` + +*Upstream documentation:* + +[Immich](https://immich.app/) is a self-hosted photo and video management solution. + +## Configuring {#module-immich-configuring} + +A complete list of options for the Immich module may be found +[here](#opt-services.immich.enable). + +### Basic setup {#module-immich-configuring-basic-setup} + +Using the following configuration you can setup a basic installation of Immich: + +```nix +{ + services.immich = { + enable = true; + + # Optionally set a different location for your media files + mediaLocation = "/my/immich/media"; + }; +} +``` + +This will configure Immich and its components as well as Postgres and +Redis databases and users for Immich. + +## Migrating from Upstream Docker Compose configuration {#module-immich-migrating-from-docker-compose} + +If you have been running Immich using the Docker Compose setup as +recommended by upstream, you can follow this guide to migrate your +existing installation to NixOS. + +This guide is going to reference variables in its examples, so make +sure to either set those in your shell or replace them with the actual +values. + +### Assumptions {#module-immich-migrating-from-docker-compose-assumptions} + +These are assumptions and goals of this migration guide: + +1. Your current installation's database name and username is `immich`. +2. Your NixOS installation's database name and username are going to + remain the same. +3. Your current library is located at `UPLOAD_LOCATION` as defined in + your current `.env` file. +4. Your NixOS installation's library will be located at + `TARGET_LOCATION`, which may be the same as `UPLOAD_LOCATION`. +5. Both `UPLOAD_LOCATION` and `TARGET_LOCATION` are absolute paths. + +### Stop current Immich installation {#module-immich-migrating-from-docker-compose-stop-immich} + +To ensure data consistency, you should stop your current Immich +instance. Otherwise, your backups/database dumps might not be in +sync. Make sure to keep your current Postgres instance running, so we +can create database dumps later. + +Example: +```console +# docker stop immich_server immich_machine_learning +``` + +### Create a backup {#module-immich-migrating-from-docker-compose-backup} + +First you want to make a backup of your existing database and photo +library. To do this, make a copy of or use a backup tool on +`UPLOAD_LOCATION`. + +Example: + +```console +# tar --create --zstd --file /var/backup/immich-library.tar.zst $UPLOAD_LOCATION +``` + +Next you should create a backup of your existing database. While this +guide is not going to modify the original database, it might still be +advisable in case something goes wrong. + +Example -- assuming your Immich database is running in a container +named `immich_postgres`: + +```console +# docker exec immich_postgres pg_dump --username immich immich > /var/backup/immich.sql +``` + +::: {.warning} +Do not add `-t`/`--tty` to the `docker` command when using it in a +pipe. Using this option will combine STDOUT and STDERR, as well as +potentially print control characters. This is undesirable when dumping +a database, as output other than the actual SQL commands might get +written to the database dump. +::: + +It is advised to store these backups in a safe location, at least +until you have confirmed that your new installation works. + +### Configure Immich NixOS module {#module-immich-migrating-from-docker-compose-configure} + +You can now enable Immich in your NixOS configuration as described in +[Configuring](#module-immich-configuring). + +After building and switching to the new configuration, you should +stop the newly started `immich-server.service` and +`immich-machine-learning.service` units. + +```console +# systemctl stop immich-server.service immich-machine-learning.service +``` + +### Create database dump {#module-immich-migrating-from-docker-compose-dump-database} + +You might have done this already during your +[backup step](#module-immich-migrating-from-docker-compose-backup). +You can choose to reuse that database dump by making a copy or create +another dump as described in that section. + +The remainder of this guide assumes that the copy of the database dump +is located at `./immich.sql`. + +::: {.note} +While `pg_dump` offers a few output formats, this guide relies on the +`plain` (default) format, as it will be modified in later steps. +::: + +### Fix up database dump {#module-immich-migrating-from-docker-compose-fixup-dump} + +The database dump generated by `pg_dump`, can most likely not be +imported as is. There are two changes we need to make, to allow it to +be imported correctly: + + +First, we need to restore value of the `search_path` variable of the +dump, as PostgreSQL will otherwise be unable to import data that +makes use of its `earthdistance` extension. +See [this StackOverflow comment](https://stackoverflow.com/questions/49702964/postgres-import-to-rds-could-not-execute-query-error-type-earth-does-not-ex#comment86441215_49702964) +and [Immich "Backup and Restore" documentation](https://immich.app/docs/administration/backup-and-restore/) +for more information. + +To work around this issue, you need to adjust the `search_path` +variable in the database dump. Run the following `sed` command to do +so: + +```console +# sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" ./immich.sql > ./immich-fixed-search-path.sql +``` + +The next issue we will need to work around is that Immich' Docker +deployment caused all library files to be referenced relative to its +working directory (`/usr/src/app` inside the container). This means +that all photos, videos and thumbnails are going to appear with the +path prefix `upload/` in the database dump, as Immich stores its data +in `./upload` relative to its working directory by default. +See [Immich "Files Custom Locations" documentation](https://immich.app/docs/guides/custom-locations/) +for more information. +In summary, the goal is to replace the `upload/` prefix with the new +*absolute* media location path `TARGET_LOCATION`. + +In order to change these file paths in the database dump, we can use +the following `sed` command: + +```console +# sed "s#upload/(.*)/#$TARGET_LOCATION/\1/#g" ./immich-fixed-search-path.sql > ./immich-final.sql +``` + +This `sed` command makes use of capture groups as just replacing every +occurrence of `upload/` would break paths referencing +`upload/upload/`. In order to replace only the prefix and not all +occurrences of `upload`, the left-hand-side also captures the path +segment after `upload/`. + +### Prepare new database and library {#module-immich-migrating-from-docker-compose-prepare-db} + +To ensure that the new database does not contain any residual data +from the brief time Immich was initially started on the system, we are going to +drop all existing data. To do so, first start an interactive `psql` +session as the Postgres superuser in the `immich` database. The +superuser is needed to create some of the extensions later; the +`immich` user will not be able to do that even in its own database. + +```console +# sudo -u postgres psql immich +psql (15.8) +Type "help" for help. + +immich=# +``` + +Now enter the following SQL code to recreate the database schemas: + +::: {.warning} +**THIS IS GOING TO DELETE ALL DATA IN THE IMMICH DATABASE**. Ensure +that you are logged into the right database and that no valuable data +is going to be deleted in the `public` and `vectors` schemas. +::: + + + +```sql +-- Drop existing schemas +DROP SCHEMA public CASCADE; +DROP SCHEMA vectors CASCADE; + +-- Create schemas and extensions +CREATE SCHEMA public; + +CREATE EXTENSION IF NOT EXISTS unaccent; +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +CREATE EXTENSION IF NOT EXISTS vectors; +CREATE EXTENSION IF NOT EXISTS cube; +CREATE EXTENSION IF NOT EXISTS earthdistance; +CREATE EXTENSION IF NOT EXISTS pg_trgm; + +-- Grant access to immich user +ALTER SCHEMA public OWNER TO immich; +ALTER SCHEMA vectors OWNER TO immich; +GRANT SELECT ON TABLE pg_vector_index_stat TO immich; + +-- Optionally "update" extension (this is technically a noop) +ALTER EXTENSION vectors UPDATE; + +-- Grant privileges to immich user +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO immich; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA vectors TO immich; +GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO immich; +GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA vectors TO immich; +``` + +Once the above statements have been executed, you may exit the +interactive `psql` console by entering `\q` or pressing `Ctrl + D`. + +Now we can import the modified database dump using the following +command: + +```console +# sudo -u postgres psql immich < ./immich-final.sql +``` + +After this command finishes, you can exit `psql` either by pressing +`Ctrl + D` or entering `\q`. + +Now as a final step, you need to ensure the correct ownership of your +media library path: + + + +```console +# chown -R immich:immich $TARGET_LOCATION +``` + +### Start migrated Immich installation {#module-immich-migrating-from-docker-compose-starting} + +Now that we prepared the database as well as the media location, we can finally start Immich and its other components: + +```console +# systemctl start immich-server.service immich-machine-learning.service +``` + +As a last step you should confirm that your previous library works +and shows up on the Web UI. You should also observe Immich's logs +closely for any missing file paths. + +You may tear down your previous Docker Compose deployment once you are +confident everything works and no loss of data occurred.