Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save vamega/74af66cfbddbeda4044f3e534ed53f5e to your computer and use it in GitHub Desktop.
Save vamega/74af66cfbddbeda4044f3e534ed53f5e to your computer and use it in GitHub Desktop.
Immich on NixOS with compose2nix issues
{
config,
lib,
pkgs,
...
}: {
imports = [
./modules-immich.nix
];
age.secrets.immichPasswordEnv = {
file = ../secrets/immichPasswordEnv.age;
};
services.immich = {
enable = true;
dbDataDirectory = "/data/fast_storage/immich/postgres";
uploadDirectory = "/data/fast_storage/immich/library";
passwordEnvFile = config.age.secrets.immichPasswordEnv.path;
};
}
DB_PASSWORD=changeme
POSTGRES_PASSWORD=changeme
Starting podman-immich_server.service...
2024-05-08 11:36:33.029575586 -0400 EDT m=+0.046308167 container create 6d10100ba5832a925d2891f8af44092b5ece0175161e7a521df05e0985896e80 (image=ghcr.io/immich-app/immich-server:release, name=immich_server, org.o>
2024-05-08 11:36:33.01124559 -0400 EDT m=+0.027978180 image pull 21c6ebb2de5315f09371cf94c484c472846c0542fc85479ffafba36a6924ec02 ghcr.io/immich-app/immich-server:release
2024-05-08 11:36:33.162045273 -0400 EDT m=+0.178777852 container init 6d10100ba5832a925d2891f8af44092b5ece0175161e7a521df05e0985896e80 (image=ghcr.io/immich-app/immich-server:release, name=immich_server, PODMAN_>
2024-05-08 11:36:33.164859533 -0400 EDT m=+0.181592118 container start 6d10100ba5832a925d2891f8af44092b5ece0175161e7a521df05e0985896e80 (image=ghcr.io/immich-app/immich-server:release, name=immich_server, org.op>
Started podman-immich_server.service.
6d10100ba5832a925d2891f8af44092b5ece0175161e7a521df05e0985896e80
Error: connect ETIMEDOUT
at Socket.<anonymous> (/usr/src/app/node_modules/ioredis/built/Redis.js:170:41)
at Object.onceWrapper (node:events:632:28)
at Socket.emit (node:events:518:28)
at Socket._onTimeout (node:net:589:8)
at listOnTimeout (node:internal/timers:573:17)
at process.processTimers (node:internal/timers:514:7) {
errorno: 'ETIMEDOUT',
code: 'ETIMEDOUT',
syscall: 'connect'
}
# Modified from what was auto-generated using compose2nix v0.1.9.
{
config,
pkgs,
lib,
...
}:
with lib; let
cfg = config.services.immich;
in {
options.services.immich = {
enable = mkEnableOption "immich";
dbDatabaseName = lib.mkOption {
type = lib.types.str;
default = "immich";
example = "immich";
description = "The postgres database name used by immich";
};
dbDataDirectory = lib.mkOption {
type = lib.types.path;
default = "/var/lib/immich/postgres";
example = "/var/lib/immich/postgres";
description = "Directory where Postgres database files are stored";
};
dbUsername = lib.mkOption {
type = lib.types.str;
default = "postgres";
example = "postgres";
description = "The name of the postgres user to use for immich";
};
immichVersion = lib.mkOption {
type = lib.types.str;
default = "release";
example = "1.7.1";
description = "The docker tag of the image to run immich under";
};
uploadDirectory = lib.mkOption {
type = lib.types.path;
default = "/var/lib/immich/upload";
example = "/var/lib/immich/upload";
description = "Path where uploaded photos are be stored";
};
passwordEnvFile = lib.mkOption {
type = lib.types.path;
default = throw "An evironment file must be specified. This has not been tested without one.";
example = "/var/lib/immich/dbPassword";
description = ''
Path to environment file that must contain the password
to the DB for the variables DB_PASSWORD and POSTGRES_PASSWORD
'';
};
port = mkOption {
type = types.port;
default = 2283;
description = "The port immich should listen on";
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = ''
Whether to open the firewall for the port in <option>services.immich.port</option>.
'';
};
};
config = let
immichEnvironment = {
DB_DATABASE_NAME = cfg.dbDatabaseName;
DB_DATA_LOCATION = cfg.dbDataDirectory;
DB_USERNAME = cfg.dbUsername;
IMMICH_VERSION = cfg.immichVersion;
UPLOAD_LOCATION = cfg.uploadDirectory;
};
postgresEnvionment = {
POSTGRES_DB = cfg.dbDatabaseName;
POSTGRES_USER = cfg.dbUsername;
};
in
mkIf cfg.enable
{
# Runtime
virtualisation.podman = {
enable = true;
autoPrune.enable = true;
dockerCompat = true;
defaultNetwork.settings = {
# Required for container networking to be able to use names.
dns_enabled = true;
};
};
virtualisation.oci-containers.backend = "podman";
# Containers
virtualisation.oci-containers.containers."immich_machine_learning" = {
image = "ghcr.io/immich-app/immich-machine-learning:release";
environment = immichEnvironment;
environmentFiles = [
cfg.passwordEnvFile
];
volumes = [
"model-cache:/cache:rw"
];
log-driver = "journald";
extraOptions = [
"--network-alias=immich-machine-learning"
"--network=immich_default"
];
};
systemd.services."podman-immich_machine_learning" = {
serviceConfig = {
Restart = lib.mkOverride 500 "always";
};
after = [
"podman-network-immich_default.service"
"podman-volume-immich_model-cache.service"
];
requires = [
"podman-network-immich_default.service"
"podman-volume-immich_model-cache.service"
];
partOf = [
"podman-compose-immich-root.target"
];
wantedBy = [
"podman-compose-immich-root.target"
];
};
virtualisation.oci-containers.containers."immich_microservices" = {
image = "ghcr.io/immich-app/immich-server:release";
environment = immichEnvironment;
environmentFiles = [
cfg.passwordEnvFile
];
volumes = [
"${cfg.uploadDirectory}:/usr/src/app/upload:rw"
"/etc/localtime:/etc/localtime:ro"
];
cmd = ["start.sh" "microservices"];
dependsOn = [
"immich_postgres"
"immich_redis"
];
log-driver = "journald";
extraOptions = [
"--network-alias=immich-microservices"
"--network=immich_default"
];
};
systemd.services."podman-immich_microservices" = {
serviceConfig = {
Restart = lib.mkOverride 500 "always";
};
after = [
"podman-network-immich_default.service"
];
requires = [
"podman-network-immich_default.service"
];
partOf = [
"podman-compose-immich-root.target"
];
unitConfig.UpheldBy = [
"podman-immich_postgres.service"
"podman-immich_redis.service"
];
wantedBy = [
"podman-compose-immich-root.target"
];
unitConfig.RequiresMountsFor = [
"${cfg.uploadDirectory}"
"/etc/localtime"
];
};
virtualisation.oci-containers.containers."immich_postgres" = {
image = "registry.hub.docker.com/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0";
environment = postgresEnvionment;
environmentFiles = [
"${cfg.passwordEnvFile}"
];
volumes = [
"${cfg.dbDataDirectory}:/var/lib/postgresql/data:rw"
];
log-driver = "journald";
extraOptions = [
"--network-alias=database"
"--network=immich_default"
];
};
systemd.services."podman-immich_postgres" = {
serviceConfig = {
Restart = lib.mkOverride 500 "always";
};
after = [
"podman-network-immich_default.service"
];
requires = [
"podman-network-immich_default.service"
];
partOf = [
"podman-compose-immich-root.target"
];
wantedBy = [
"podman-compose-immich-root.target"
];
unitConfig.RequiresMountsFor = [
"${cfg.dbDataDirectory}"
];
};
virtualisation.oci-containers.containers."immich_redis" = {
image = "registry.hub.docker.com/library/redis:6.2-alpine@sha256:84882e87b54734154586e5f8abd4dce69fe7311315e2fc6d67c29614c8de2672";
log-driver = "journald";
extraOptions = [
"--network-alias=redis"
"--network=immich_default"
];
};
systemd.services."podman-immich_redis" = {
serviceConfig = {
Restart = lib.mkOverride 500 "always";
};
after = [
"podman-network-immich_default.service"
];
requires = [
"podman-network-immich_default.service"
];
partOf = [
"podman-compose-immich-root.target"
];
wantedBy = [
"podman-compose-immich-root.target"
];
};
virtualisation.oci-containers.containers."immich_server" = {
image = "ghcr.io/immich-app/immich-server:release";
environment = immichEnvironment;
environmentFiles = [
"${cfg.passwordEnvFile}"
];
volumes = [
"${cfg.uploadDirectory}:/usr/src/app/upload:rw"
"/etc/localtime:/etc/localtime:ro"
];
ports = [
"${toString cfg.port}:3001/tcp"
];
cmd = ["start.sh" "immich"];
dependsOn = [
"immich_postgres"
"immich_redis"
];
log-driver = "journald";
extraOptions = [
"--network-alias=immich-server"
"--network=immich_default"
];
};
systemd.services."podman-immich_server" = {
serviceConfig = {
Restart = lib.mkOverride 500 "always";
};
after = [
"podman-network-immich_default.service"
];
requires = [
"podman-network-immich_default.service"
];
partOf = [
"podman-compose-immich-root.target"
];
upheldBy = [
"podman-immich_postgres.service"
"podman-immich_redis.service"
];
wantedBy = [
"podman-compose-immich-root.target"
];
unitConfig.RequiresMountsFor = [
"${cfg.uploadDirectory}"
"/etc/localtime"
];
};
# Networks
systemd.services."podman-network-immich_default" = {
path = [pkgs.podman];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStop = "${pkgs.podman}/bin/podman network rm -f immich_default";
};
script = ''
podman network inspect immich_default || podman network create immich_default
'';
partOf = ["podman-compose-immich-root.target"];
wantedBy = ["podman-compose-immich-root.target"];
};
# Volumes
systemd.services."podman-volume-immich_model-cache" = {
path = [pkgs.podman];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
podman volume inspect immich_model-cache || podman volume create immich_model-cache
'';
partOf = ["podman-compose-immich-root.target"];
wantedBy = ["podman-compose-immich-root.target"];
};
# Root service
# When started, this will automatically create all resources and start
# the containers. When stopped, this will teardown all resources.
systemd.targets."podman-compose-immich-root" = {
unitConfig = {
Description = "Root target generated by compose2nix.";
};
wantedBy = ["multi-user.target"];
};
# Firewall
networking.firewall.allowedTCPPorts = mkIf (cfg.openFirewall) [cfg.port];
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment