[Feature]: Support native deployments (without docker, includes example) #323

Closed
opened 2026-02-04 19:38:32 +03:00 by OVERLORD · 9 comments
Owner

Originally created by @oddlama on GitHub (Sep 22, 2022).

Feature detail

Thanks for the great app! One thing that I'm dearly missing is the ability to deploy this on bare-metal, without relying on docker. The problem is that in certain situations, docker might not be a viable option to deploy a project. I for example am using an extremely strict nftables firewall, for which docker simply has no compatibility (and docker will likely still take some years to catch up). Furthermore, I deploy each application in its own KVM virtual machine with libvirt, so docker would just be an unnecessary overhead for me.

I have already managed to get immich running without docker just fine, it would just be missing a tiny bit of packaging to make the process more accessible and repeatable. I've attached my scripts to build and deploy immich below in case you (or any other people stumbling over this) are interested.

Installing on bare-metal

  1. Create user for immich (/var/lib/immich)
  2. Create environment file /var/lib/immich/env (adapt from upstream provided .env.example)
  3. Build app and create links to upload folder with script runuser -u immich -- ./build.sh
  4. Map container aliases to localhost in /etc/hosts like this: 127.0.0.1 localhost immich-server immich-microservices immich-machine-learning immich-web
  5. Create fstab entry to bind mount /the/backup/folder/you/want to /var/lib/immich/upload
  6. Start redis, postgresql and provided systemd services

The need to map the upload folder in several app directories is a little clunky right now, just like having to use /etc/hosts because hostnames are hardcoded. But the rest is working like a charm without docker.

You might need to change the systemd services a little to fit your system if e.g. your postgresql service is not called postgresql-14.service, or when it is running on another system. I've also used systemd to do sandboxing for the services, so they will not be able to access other directories if you need that for some reason.

Buildscript build.sh

This script is adapted from the Dockerfiles I found in this repo.

#!/bin/bash

IMMICH_TAG=v1.29.4_44-dev

set -uo pipefail
function die() { echo "error: $*" >&2; exit 1; }
function try() { "$@" || die "Command failed: $*"; }

TMP="$(mktemp --tmpdir=/var/tmp -d)" || die "Could not create tmpdir"
trap 'rm -rf -- "$TMP"' EXIT

try cd "$TMP"
try git clone https://github.com/immich-app/immich
try cd "$TMP"/immich
try git checkout "$IMMICH_TAG"

# Build server
try cd "$TMP"/immich/server
try npm ci
try npm run build
try npm prune --production
try mkdir app
try cp -a package.json package-lock.json node_modules dist app

# Build web frontend
try cd "$TMP"/immich/web
try npm ci
try npm run build
# TODO: why is the whole folder used here? Can we strip something from here? What about npm prune --production?

# Build machine-learning
try cd "$TMP"/immich/machine-learning
try npm ci
try npm rebuild @tensorflow/tfjs-node --build-from-source
try npm run build
try npm prune --production
try mkdir app
try cp -a package.json package-lock.json node_modules dist app

# Copy only relevant files to new directory
try cd "$TMP"/
try mkdir app
try mv "$TMP"/immich/server/app app/server
try mv "$TMP"/immich/web app/web
try mv "$TMP"/immich/machine-learning/app app/machine-learning

# Install new files (deleting old deployment)
[[ -e /var/lib/immich/app ]] && try rm -rf /var/lib/immich/app
try cp -r app /var/lib/immich/app

# Ensure the upload folder is available
mkdir -p /var/lib/immich/upload
ln -s /var/lib/immich/upload /var/lib/immich/app/server/upload
ln -s /var/lib/immich/upload /var/lib/immich/app/machine-learning/upload

Systemd service immich-server.service

[Unit]
Description=immich server
Documentation=https://github.com/immich-app/immich
Requires=redis-immich.service
Requires=postgresql-14.service
After=network.target
Wants=network-online.target
After=network-online.target

[Service]
User=immich
Group=immich
Type=simple
Restart=on-failure

EnvironmentFile=/var/lib/immich/env
Environment=NODE_ENV=production
SyslogIdentifier=immich-server
ExecStart=node dist/apps/immich/apps/immich/src/main

PrivateDevices=true
ProtectHome=true
ProtectSystem=strict
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes

RestrictNamespaces=yes

SystemCallArchitectures=native
AmbientCapabilities=
CapabilityBoundingSet=
NoNewPrivileges=yes

WorkingDirectory=/var/lib/immich/app/server
ReadWritePaths=/tmp /var/tmp /var/lib/immich/upload

[Install]
WantedBy=multi-user.target

Systemd service immich-microservices.service

[Unit]
Description=immich microservices
Documentation=https://github.com/immich-app/immich
Requires=redis-immich.service
Requires=postgresql-14.service
After=network.target
Wants=network-online.target
After=network-online.target

[Service]
User=immich
Group=immich
Type=simple
Restart=on-failure

EnvironmentFile=/var/lib/immich/env
Environment=NODE_ENV=production
SyslogIdentifier=immich-microservices
ExecStart=node dist/apps/microservices/apps/microservices/src/main

PrivateDevices=true
ProtectHome=true
ProtectSystem=strict
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes

RestrictNamespaces=yes

SystemCallArchitectures=native
AmbientCapabilities=
CapabilityBoundingSet=
NoNewPrivileges=yes

WorkingDirectory=/var/lib/immich/app/server
ReadWritePaths=/tmp /var/tmp /var/lib/immich/upload

[Install]
WantedBy=multi-user.target

Systemd service immich-machine-learning.service

[Unit]
Description=immich machine learning
Documentation=https://github.com/immich-app/immich
Requires=postgresql-14.service
After=network.target
Wants=network-online.target
After=network-online.target

[Service]
User=immich
Group=immich
Type=simple
Restart=on-failure

EnvironmentFile=/var/lib/immich/env
Environment=NODE_ENV=production
SyslogIdentifier=immich-machine-learning
ExecStart=node dist/main.js

PrivateDevices=true
ProtectHome=true
ProtectSystem=strict
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes

RestrictNamespaces=yes

SystemCallArchitectures=native
AmbientCapabilities=
CapabilityBoundingSet=
NoNewPrivileges=yes

WorkingDirectory=/var/lib/immich/app/machine-learning
ReadWritePaths=/tmp /var/tmp /var/lib/immich/upload

[Install]
WantedBy=multi-user.target

Systemd service immich-web.service

[Unit]
Description=immich web
Documentation=https://github.com/immich-app/immich
After=network.target
Wants=network-online.target
After=network-online.target

[Service]
User=immich
Group=immich
Type=simple
Restart=on-failure

EnvironmentFile=/var/lib/immich/env
Environment=NODE_ENV=production
SyslogIdentifier=immich-web
ExecStart=node build/index.js

PrivateDevices=true
ProtectHome=true
ProtectSystem=strict
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes

RestrictNamespaces=yes

SystemCallArchitectures=native
AmbientCapabilities=
CapabilityBoundingSet=
NoNewPrivileges=yes

WorkingDirectory=/var/lib/immich/app/web
ReadWritePaths=/tmp /var/tmp

[Install]
WantedBy=multi-user.target

P.S.: Is it intended that compiling the most recent release with npm reports 10+ high and 3 critical security issues? Not sure what to do about that as a user.

Platform

Server

Originally created by @oddlama on GitHub (Sep 22, 2022). ### Feature detail Thanks for the great app! One thing that I'm dearly missing is the ability to deploy this on bare-metal, without relying on docker. The problem is that in certain situations, docker might not be a viable option to deploy a project. I for example am using an extremely strict nftables firewall, for which docker simply has no compatibility (and docker will likely still take some years to catch up). Furthermore, I deploy each application in its own KVM virtual machine with libvirt, so docker would just be an unnecessary overhead for me. I have already managed to get immich running without docker just fine, it would just be missing a tiny bit of packaging to make the process more accessible and repeatable. I've attached my scripts to build and deploy immich below in case you (or any other people stumbling over this) are interested. ## Installing on bare-metal 1. Create user for immich (`/var/lib/immich`) 2. Create environment file `/var/lib/immich/env` (adapt from upstream provided `.env.example`) 3. Build app and create links to upload folder with script `runuser -u immich -- ./build.sh` 4. Map container aliases to localhost in /etc/hosts like this: `127.0.0.1 localhost immich-server immich-microservices immich-machine-learning immich-web` 5. Create fstab entry to bind mount `/the/backup/folder/you/want` to `/var/lib/immich/upload` 6. Start redis, postgresql and provided systemd services The need to map the upload folder in several app directories is a little clunky right now, just like having to use /etc/hosts because hostnames are hardcoded. But the rest is working like a charm without docker. You might need to change the systemd services a little to fit your system if e.g. your postgresql service is not called `postgresql-14.service`, or when it is running on another system. I've also used systemd to do sandboxing for the services, so they will not be able to access other directories if you need that for some reason. ### Buildscript `build.sh` This script is adapted from the Dockerfiles I found in this repo. ```bash #!/bin/bash IMMICH_TAG=v1.29.4_44-dev set -uo pipefail function die() { echo "error: $*" >&2; exit 1; } function try() { "$@" || die "Command failed: $*"; } TMP="$(mktemp --tmpdir=/var/tmp -d)" || die "Could not create tmpdir" trap 'rm -rf -- "$TMP"' EXIT try cd "$TMP" try git clone https://github.com/immich-app/immich try cd "$TMP"/immich try git checkout "$IMMICH_TAG" # Build server try cd "$TMP"/immich/server try npm ci try npm run build try npm prune --production try mkdir app try cp -a package.json package-lock.json node_modules dist app # Build web frontend try cd "$TMP"/immich/web try npm ci try npm run build # TODO: why is the whole folder used here? Can we strip something from here? What about npm prune --production? # Build machine-learning try cd "$TMP"/immich/machine-learning try npm ci try npm rebuild @tensorflow/tfjs-node --build-from-source try npm run build try npm prune --production try mkdir app try cp -a package.json package-lock.json node_modules dist app # Copy only relevant files to new directory try cd "$TMP"/ try mkdir app try mv "$TMP"/immich/server/app app/server try mv "$TMP"/immich/web app/web try mv "$TMP"/immich/machine-learning/app app/machine-learning # Install new files (deleting old deployment) [[ -e /var/lib/immich/app ]] && try rm -rf /var/lib/immich/app try cp -r app /var/lib/immich/app # Ensure the upload folder is available mkdir -p /var/lib/immich/upload ln -s /var/lib/immich/upload /var/lib/immich/app/server/upload ln -s /var/lib/immich/upload /var/lib/immich/app/machine-learning/upload ``` ### Systemd service `immich-server.service` ```systemd.service [Unit] Description=immich server Documentation=https://github.com/immich-app/immich Requires=redis-immich.service Requires=postgresql-14.service After=network.target Wants=network-online.target After=network-online.target [Service] User=immich Group=immich Type=simple Restart=on-failure EnvironmentFile=/var/lib/immich/env Environment=NODE_ENV=production SyslogIdentifier=immich-server ExecStart=node dist/apps/immich/apps/immich/src/main PrivateDevices=true ProtectHome=true ProtectSystem=strict ProtectKernelTunables=yes ProtectKernelModules=yes ProtectControlGroups=yes RestrictNamespaces=yes SystemCallArchitectures=native AmbientCapabilities= CapabilityBoundingSet= NoNewPrivileges=yes WorkingDirectory=/var/lib/immich/app/server ReadWritePaths=/tmp /var/tmp /var/lib/immich/upload [Install] WantedBy=multi-user.target ``` ### Systemd service `immich-microservices.service` ```systemd.service [Unit] Description=immich microservices Documentation=https://github.com/immich-app/immich Requires=redis-immich.service Requires=postgresql-14.service After=network.target Wants=network-online.target After=network-online.target [Service] User=immich Group=immich Type=simple Restart=on-failure EnvironmentFile=/var/lib/immich/env Environment=NODE_ENV=production SyslogIdentifier=immich-microservices ExecStart=node dist/apps/microservices/apps/microservices/src/main PrivateDevices=true ProtectHome=true ProtectSystem=strict ProtectKernelTunables=yes ProtectKernelModules=yes ProtectControlGroups=yes RestrictNamespaces=yes SystemCallArchitectures=native AmbientCapabilities= CapabilityBoundingSet= NoNewPrivileges=yes WorkingDirectory=/var/lib/immich/app/server ReadWritePaths=/tmp /var/tmp /var/lib/immich/upload [Install] WantedBy=multi-user.target ``` ### Systemd service `immich-machine-learning.service` ```systemd.service [Unit] Description=immich machine learning Documentation=https://github.com/immich-app/immich Requires=postgresql-14.service After=network.target Wants=network-online.target After=network-online.target [Service] User=immich Group=immich Type=simple Restart=on-failure EnvironmentFile=/var/lib/immich/env Environment=NODE_ENV=production SyslogIdentifier=immich-machine-learning ExecStart=node dist/main.js PrivateDevices=true ProtectHome=true ProtectSystem=strict ProtectKernelTunables=yes ProtectKernelModules=yes ProtectControlGroups=yes RestrictNamespaces=yes SystemCallArchitectures=native AmbientCapabilities= CapabilityBoundingSet= NoNewPrivileges=yes WorkingDirectory=/var/lib/immich/app/machine-learning ReadWritePaths=/tmp /var/tmp /var/lib/immich/upload [Install] WantedBy=multi-user.target ``` ### Systemd service `immich-web.service` ```systemd.service [Unit] Description=immich web Documentation=https://github.com/immich-app/immich After=network.target Wants=network-online.target After=network-online.target [Service] User=immich Group=immich Type=simple Restart=on-failure EnvironmentFile=/var/lib/immich/env Environment=NODE_ENV=production SyslogIdentifier=immich-web ExecStart=node build/index.js PrivateDevices=true ProtectHome=true ProtectSystem=strict ProtectKernelTunables=yes ProtectKernelModules=yes ProtectControlGroups=yes RestrictNamespaces=yes SystemCallArchitectures=native AmbientCapabilities= CapabilityBoundingSet= NoNewPrivileges=yes WorkingDirectory=/var/lib/immich/app/web ReadWritePaths=/tmp /var/tmp [Install] WantedBy=multi-user.target ``` --- P.S.: Is it intended that compiling the most recent release with npm reports 10+ high and 3 critical security issues? Not sure what to do about that as a user. ### Platform Server
OVERLORD added the documentation label 2026-02-04 19:38:32 +03:00
Author
Owner

@StillLoading commented on GitHub (Sep 24, 2022):

Thank you for this. Really appreciate the work you put into sorting this out.

For anyone wanting to deploy on OpenSUSE Tumbleweed/MicroOS here are some pointers.

Dependencies

For building

make gcc gcc-c++ npm18 nodejs18 nodejs18-devel nodejs-common

For hosting

nginx redis postgresql-server

SELinux

Allow local nginx proxy to connect to local services via port

setsebool -P httpd_can_network_connect 1

Nginx

Grab the config from this repository and modify to your needs.

@StillLoading commented on GitHub (Sep 24, 2022): Thank you for this. Really appreciate the work you put into sorting this out. For anyone wanting to deploy on OpenSUSE Tumbleweed/MicroOS here are some pointers. ## Dependencies For building ``` make gcc gcc-c++ npm18 nodejs18 nodejs18-devel nodejs-common ``` For hosting ``` nginx redis postgresql-server ``` ## SELinux Allow local nginx proxy to connect to local services via port ``` setsebool -P httpd_can_network_connect 1 ``` ## Nginx Grab the config from this repository and modify to your needs.
Author
Owner

@huangfengjing commented on GitHub (Sep 30, 2022):

Nice work.
Can anybody compose a Batch-Script/PowerShell script like this to help deploying immich on Windows?

@huangfengjing commented on GitHub (Sep 30, 2022): Nice work. Can anybody compose a Batch-Script/PowerShell script like this to help deploying immich on Windows?
Author
Owner

@jumbi77 commented on GitHub (Oct 3, 2022):

Thanks a lot.
One question: Is this Requires=redis-immich.service correct in systemd.services? My system cant find those and i had to change them Requires=redis-server.service

@jumbi77 commented on GitHub (Oct 3, 2022): Thanks a lot. One question: Is this `Requires=redis-immich.service` correct in systemd.services? My system cant find those and i had to change them `Requires=redis-server.service`
Author
Owner

@StillLoading commented on GitHub (Oct 3, 2022):

@jumbi77 I think @oddlama did a good job at pointing that out in his post.

You might need to change the systemd services a little to fit your system if e.g. your postgresql service is not called postgresql-14.service, or when it is running on another system...

@StillLoading commented on GitHub (Oct 3, 2022): @jumbi77 I think @oddlama did a good job at pointing that out in his post. > You might need to change the systemd services a little to fit your system if e.g. your postgresql service is not called `postgresql-14.service`, or when it is running on another system...
Author
Owner

@hydazz commented on GitHub (Dec 26, 2022):

(off topic)
using this installing on bare-metal method, I am creating a (hopefully) functioning single immich container for me to use on unraid (docker-compose is really messy). main issue is that the server expects hostnames immich-server immich-microservices immich-machine-learning immich-web and the hosts file cant be modified on docker so I end up with

Error: getaddrinfo ENOTFOUND immich-server

https://github.com/hydazz/docker-immich
#appsshouldbeasignecontainerwhereapplicable

edit: all seems to be working in latest commit...

@hydazz commented on GitHub (Dec 26, 2022): (off topic) using this installing on bare-metal method, I am creating a (hopefully) functioning single immich container for me to use on unraid (docker-compose is really messy). main issue is that the server expects hostnames `immich-server` `immich-microservices` `immich-machine-learning` `immich-web` and the `hosts` file cant be modified on docker so I end up with `Error: getaddrinfo ENOTFOUND immich-server` https://github.com/hydazz/docker-immich #appsshouldbeasignecontainerwhereapplicable edit: all seems to be working in latest commit...
Author
Owner

@alextran1502 commented on GitHub (Dec 26, 2022):

(off topic) using this installing on bare-metal method, I am creating a (hopefully) functioning single immich container for me to use on unraid (docker-compose is really messy). main issue is that the server expects hostnames immich-server immich-microservices immich-machine-learning immich-web and the hosts file cant be modified on docker so I end up with

Error: getaddrinfo ENOTFOUND immich-server

https://github.com/hydazz/docker-immich #appsshouldbeasignecontainerwhereapplicable

edit: all seems to be working in latest commit...

Thank you for the contribution.

I have a thought about your hashtag #appsshouldbeasignecontainerwhereapplicable

Let's say you want to limit the CPU of machine-learning and microservices containers which handle the intensive background tasks, so that your core server can still handle I/O operations effectively and efficiently while there are people performing backing up. Can you do that with the single container image?

@alextran1502 commented on GitHub (Dec 26, 2022): > (off topic) using this installing on bare-metal method, I am creating a (hopefully) functioning single immich container for me to use on unraid (docker-compose is really messy). main issue is that the server expects hostnames `immich-server` `immich-microservices` `immich-machine-learning` `immich-web` and the `hosts` file cant be modified on docker so I end up with > > `Error: getaddrinfo ENOTFOUND immich-server` > > https://github.com/hydazz/docker-immich #appsshouldbeasignecontainerwhereapplicable > > edit: all seems to be working in latest commit... Thank you for the contribution. I have a thought about your hashtag #appsshouldbeasignecontainerwhereapplicable Let's say you want to limit the CPU of `machine-learning` and `microservices` containers which handle the intensive background tasks, so that your core server can still handle I/O operations effectively and efficiently while there are people performing backing up. Can you do that with the single container image?
Author
Owner

@hydazz commented on GitHub (Dec 27, 2022):

Let's say you want to limit the CPU of machine-learning and microservices containers which handle the intensive background tasks, so that your core server can still handle I/O operations effectively and efficiently while there are people performing backing up. Can you do that with the single container image?

Yes, that's not really possible on a single container - there are indeed use cases for having the containers separated on lower-powered hardware. Most self-hosted image services have single containers which makes deployment much easier on specific OSes (ie Unraid), where most users won't even think about limiting the CPU of a container.

@hydazz commented on GitHub (Dec 27, 2022): > Let's say you want to limit the CPU of `machine-learning` and `microservices` containers which handle the intensive background tasks, so that your core server can still handle I/O operations effectively and efficiently while there are people performing backing up. Can you do that with the single container image? Yes, that's not really possible on a single container - there are indeed use cases for having the containers separated on lower-powered hardware. Most self-hosted image services have single containers which makes deployment much easier on specific OSes (ie Unraid), where most users won't even think about limiting the CPU of a container.
Author
Owner

@bt90 commented on GitHub (Dec 30, 2022):

You could try to start the container with the sys_nice capability and change the process priorities inside the container to accomplish that.

https://docs.docker.com/config/containers/resource_constraints/#configure-individual-containers

Pair that with e.g dumb-init:

https://github.com/Yelp/dumb-init#session-behavior

@bt90 commented on GitHub (Dec 30, 2022): You could try to start the container with the `sys_nice` capability and change the process priorities inside the container to accomplish that. https://docs.docker.com/config/containers/resource_constraints/#configure-individual-containers Pair that with e.g dumb-init: https://github.com/Yelp/dumb-init#session-behavior
Author
Owner

@hydazz commented on GitHub (Dec 31, 2022):

You could try to start the container with the sys_nice capability and change the process priorities inside the container to accomplish that.

https://docs.docker.com/config/containers/resource_constraints/#configure-individual-containers

Pair that with e.g dumb-init:

https://github.com/Yelp/dumb-init#session-behavior

there should be a way to achieve this with s6 and s6-overlay

Why does the machine-learning module spawn 40+ processes that use up over 1GB of ram at idle? is this necessary? is it possible to have the machine-learning run on-demand rather than all the time?
image

@hydazz commented on GitHub (Dec 31, 2022): > You could try to start the container with the `sys_nice` capability and change the process priorities inside the container to accomplish that. > > https://docs.docker.com/config/containers/resource_constraints/#configure-individual-containers > > Pair that with e.g dumb-init: > > https://github.com/Yelp/dumb-init#session-behavior there should be a way to achieve this with s6 and s6-overlay Why does the machine-learning module spawn 40+ processes that use up over 1GB of ram at idle? is this necessary? is it possible to have the machine-learning run on-demand rather than all the time? ![image](https://user-images.githubusercontent.com/53986978/210122124-ac144366-bf82-48d9-96b5-d46a5896c2f5.png)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: immich-app/immich#323