Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/doc/immich: init #344300

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
269 changes: 269 additions & 0 deletions nixos/modules/services/web-apps/immich.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
# Immich {#module-immich}

*Source:* {file}`modules/services/web-apps/immich.nix`

*Upstream documentation:* <https://immich.app/docs/overview/introduction/>

[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}
Scrumplex marked this conversation as resolved.
Show resolved Hide resolved

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.
Scrumplex marked this conversation as resolved.
Show resolved Hide resolved

### 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:

<!-- TODO: Is the following actually a Postgres bug? -->
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
Scrumplex marked this conversation as resolved.
Show resolved Hide resolved
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.
:::

<!-- Ensure this snippet is up to date with the module. -->
<!-- TODO: Provide a migration script that utilizes the init script generated by the module? -->
```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
Scrumplex marked this conversation as resolved.
Show resolved Hide resolved
```

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:

<!-- Do NOT add a / to the end of this example command, as running it
with an unset variable will cause everything on the rootfs to
change ownership! -->
<!-- Perhaps this should have a warning about this? -->
```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.