diff --git a/.devcontainer/.gitignore b/.devcontainer/.gitignore deleted file mode 100644 index 6bf3b5d9e5..0000000000 --- a/.devcontainer/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.env -library \ No newline at end of file diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 4ce7076011..0000000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:7c2e711a4f7b02f32d2da16192d5e05aa7c95279be4ce889cff5df316f251c1d -FROM ${BASEIMAGE} - -# Flutter SDK -# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux -ENV FLUTTER_CHANNEL="stable" -ENV FLUTTER_VERSION="3.29.3" -ENV FLUTTER_HOME=/flutter -ENV PATH=${PATH}:${FLUTTER_HOME}/bin - -# Flutter SDK -RUN mkdir -p ${FLUTTER_HOME} \ - && curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \ - && tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \ - && rm flutter.tar.xz \ - && chown -R 1000:1000 ${FLUTTER_HOME} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 2d567f033a..4e4285f131 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,26 +1,67 @@ { - "name": "Immich", - "service": "immich-devcontainer", + "name": "Immich - Backend, Frontend and ML", + "service": "immich-server", + "runServices": [ + "immich-server", + "redis", + "database", + "immich-machine-learning" + ], "dockerComposeFile": [ - "docker-compose.yml", - "../docker/docker-compose.dev.yml" + "../docker/docker-compose.dev.yml", + "./server/container-compose-overrides.yml" ], "customizations": { "vscode": { "extensions": [ - "Dart-Code.dart-code", - "Dart-Code.flutter", "dbaeumer.vscode-eslint", - "dcmdev.dcm-vscode-extension", "esbenp.prettier-vscode", - "svelte.svelte-vscode" + "svelte.svelte-vscode", + "ms-vscode-remote.remote-containers", + "foxundermoon.shell-format", + "timonwong.shellcheck", + "rvest.vs-code-prettier-eslint", + "bluebrown.yamlfmt", + "vkrishna04.cspell-sync", + "vitest.explorer", + "ms-playwright.playwright", + "ms-azuretools.vscode-docker" ] } }, - "forwardPorts": [], - "initializeCommand": "bash .devcontainer/scripts/initializeCommand.sh", - "onCreateCommand": "bash .devcontainer/scripts/onCreateCommand.sh", + "forwardPorts": [3000, 9231, 9230, 2283], + "portsAttributes": { + "3000": { + "label": "Immich - Frontend HTTP", + "description": "The frontend of the Immich project", + "onAutoForward": "openBrowserOnce" + }, + "2283": { + "label": "Immich - API Server - HTTP", + "description": "The API server of the Immich project" + }, + "9231": { + "label": "Immich - API Server - DEBUG", + "description": "The API server of the Immich project" + }, + "9230": { + "label": "Immich - Workers - DEBUG", + "description": "The workers of the Immich project" + } + }, "overrideCommand": true, - "workspaceFolder": "/immich", - "remoteUser": "node" + "workspaceFolder": "/workspaces/immich", + "remoteUser": "node", + "userEnvProbe": "loginInteractiveShell", + "remoteEnv": { + // The location where your uploaded files are stored + "UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}", + // Connection secret for postgres. You should change it to a random password + // Please use only the characters `A-Za-z0-9`, without special characters or spaces + "DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}", + // The database username + "DB_USERNAME": "${localEnv:DB_USERNAME:postgres}", + // The database name + "DB_DATABASE_NAME": "${localEnv:DB_DATABASE_NAME:immich}" + } } diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml deleted file mode 100644 index 25719641d2..0000000000 --- a/.devcontainer/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -services: - immich-devcontainer: - build: - dockerfile: Dockerfile - extra_hosts: - - 'host.docker.internal:host-gateway' - volumes: - - ..:/immich:cached diff --git a/.devcontainer/mobile/container-compose-overrides.yml b/.devcontainer/mobile/container-compose-overrides.yml new file mode 100644 index 0000000000..62a97a01eb --- /dev/null +++ b/.devcontainer/mobile/container-compose-overrides.yml @@ -0,0 +1,34 @@ +services: + immich-server: + build: + target: dev-container-mobile + environment: + - IMMICH_SERVER_URL=http://127.0.0.1:2283/ + volumes: !override # bind mount host to /workspaces/immich + - ..:/workspaces/immich + - cli_node_modules:/workspaces/immich/cli/node_modules + - e2e_node_modules:/workspaces/immich/e2e/node_modules + - open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules + - server_node_modules:/workspaces/immich/server/node_modules + - web_node_modules:/workspaces/immich/web/node_modules + - ${UPLOAD_LOCATION}/photos:/workspaces/immich/server/upload + - ${UPLOAD_LOCATION}/photos/upload:/workspaces/immich/server/upload/upload + - /etc/localtime:/etc/localtime:ro + + database: + volumes: + - ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data + +volumes: + # Node modules for each service to avoid conflicts and ensure consistent dependencies + cli_node_modules: + e2e_node_modules: + open_api_node_modules: + server_node_modules: + web_node_modules: + + # UPLOAD_LOCATION must be set to a absolute path or vol-upload + vol-upload: + + # DB_DATA_LOCATION must be set to a absolute path or vol-database + vol-database: diff --git a/.devcontainer/mobile/devcontainer.json b/.devcontainer/mobile/devcontainer.json new file mode 100644 index 0000000000..0dbcc8e9c8 --- /dev/null +++ b/.devcontainer/mobile/devcontainer.json @@ -0,0 +1,52 @@ +{ + "name": "Immich - Mobile", + "service": "immich-server", + "runServices": [ + "immich-server", + "redis", + "database", + "immich-machine-learning" + ], + "dockerComposeFile": [ + "../../docker/docker-compose.dev.yml", + "./container-compose-overrides.yml" + ], + "customizations": { + "vscode": { + "extensions": [ + "Dart-Code.dart-code", + "Dart-Code.flutter", + "dcmdev.dcm-vscode-extension", + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "svelte.svelte-vscode", + "ms-vscode-remote.remote-containers", + "foxundermoon.shell-format", + "timonwong.shellcheck", + "rvest.vs-code-prettier-eslint", + "bluebrown.yamlfmt", + "vkrishna04.cspell-sync", + "vitest.explorer", + "ms-playwright.playwright", + "ms-azuretools.vscode-docker" + ] + } + }, + "forwardPorts": [], + "overrideCommand": true, + "workspaceFolder": "/workspaces/immich", + "remoteUser": "node", + "userEnvProbe": "loginInteractiveShell", + "remoteEnv": { + // The location where your uploaded files are stored + "UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}", + // Connection secret for postgres. You should change it to a random password + // Please use only the characters `A-Za-z0-9`, without special characters or spaces + "DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}", + // The database username + "DB_USERNAME": "${localEnv:DB_USERNAME:postgres}", + // The database name + "DB_DATABASE_NAME": "${localEnv:DB_DATABASE_NAME:immich}" + } +} diff --git a/.devcontainer/scripts/initializeCommand.sh b/.devcontainer/scripts/initializeCommand.sh deleted file mode 100644 index 9d9d196696..0000000000 --- a/.devcontainer/scripts/initializeCommand.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -# If .env file does not exist, create it by copying example.env from the docker folder -if [ ! -f ".devcontainer/.env" ]; then - cp docker/example.env .devcontainer/.env -fi diff --git a/.devcontainer/scripts/onCreateCommand.sh b/.devcontainer/scripts/onCreateCommand.sh deleted file mode 100644 index 2f898ec32e..0000000000 --- a/.devcontainer/scripts/onCreateCommand.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -# Enable multiarch for arm64 if necessary -if [ "$(dpkg --print-architecture)" = "arm64" ]; then - sudo dpkg --add-architecture amd64 && \ - sudo apt-get update && \ - sudo apt-get install -y --no-install-recommends \ - qemu-user-static \ - libc6:amd64 \ - libstdc++6:amd64 \ - libgcc1:amd64 -fi - -# Install DCM -wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg -sudo echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list - -sudo apt-get update -sudo apt-get install dcm - -dart --disable-analytics - -# Install immich -cd /immich || exit -make install-all diff --git a/.devcontainer/server/container-common.sh b/.devcontainer/server/container-common.sh new file mode 100755 index 0000000000..95f4e222a1 --- /dev/null +++ b/.devcontainer/server/container-common.sh @@ -0,0 +1,57 @@ +#!/bin/bash +export IMMICH_PORT="${DEV_SERVER_PORT:-2283}" +export DEV_PORT="${DEV_PORT:-3000}" + +# search for immich directory inside workspace. +# /workspaces/immich is the bind mount, but other directories can be mounted if runing +# Devcontainer: Clone [repository|pull request] in container volumne +WORKSPACES_DIR="/workspaces" +IMMICH_DIR="$WORKSPACES_DIR/immich" + +# Find directories excluding /workspaces/immich +mapfile -t other_dirs < <(find "$WORKSPACES_DIR" -mindepth 1 -maxdepth 1 -type d ! -path "$IMMICH_DIR" ! -name ".*") + +if [ ${#other_dirs[@]} -gt 1 ]; then + echo "Error: More than one directory found in $WORKSPACES_DIR other than $IMMICH_DIR." + exit 1 +elif [ ${#other_dirs[@]} -eq 1 ]; then + export IMMICH_WORKSPACE="${other_dirs[0]}" +else + export IMMICH_WORKSPACE="$IMMICH_DIR" +fi + +echo "Found immich workspace in $IMMICH_WORKSPACE" + +run_cmd() { + echo "$@" + "$@" +} + +fix_permissions() { + + echo "Fixing permissions for ${IMMICH_WORKSPACE}" + + run_cmd sudo find "${IMMICH_WORKSPACE}/server/upload" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres/*" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres" -exec chown node {} + + + run_cmd sudo chown node -R "${IMMICH_WORKSPACE}/.vscode" \ + "${IMMICH_WORKSPACE}/cli/node_modules" \ + "${IMMICH_WORKSPACE}/e2e/node_modules" \ + "${IMMICH_WORKSPACE}/open-api/typescript-sdk/node_modules" \ + "${IMMICH_WORKSPACE}/server/node_modules" \ + "${IMMICH_WORKSPACE}/server/dist" \ + "${IMMICH_WORKSPACE}/web/node_modules" \ + "${IMMICH_WORKSPACE}/web/dist" +} + +install_dependencies() { + + echo "Installing dependencies" + + ( + cd "${IMMICH_WORKSPACE}" || exit 1 + run_cmd make install-server + run_cmd make install-open-api + run_cmd make build-open-api + run_cmd make install-web + ) +} \ No newline at end of file diff --git a/.devcontainer/server/container-compose-overrides.yml b/.devcontainer/server/container-compose-overrides.yml new file mode 100644 index 0000000000..94fbbab8cd --- /dev/null +++ b/.devcontainer/server/container-compose-overrides.yml @@ -0,0 +1,44 @@ +services: + immich-server: + build: + target: dev-container-server + env_file: !reset [] + environment: + - IMMICH_SERVER_URL=http://127.0.0.1:2283/ + volumes: !override + - ..:/workspaces/immich + - cli_node_modules:/workspaces/immich/cli/node_modules + - e2e_node_modules:/workspaces/immich/e2e/node_modules + - open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules + - server_node_modules:/workspaces/immich/server/node_modules + - web_node_modules:/workspaces/immich/web/node_modules + - ${UPLOAD_LOCATION-./Library}/photos:/workspaces/immich/server/upload + - ${UPLOAD_LOCATION-./Library}/photos/upload:/workspaces/immich/server/upload/upload + - /etc/localtime:/etc/localtime:ro + + immich-web: + env_file: !reset [] + + immich-machine-learning: + env_file: !reset [] + + database: + env_file: !reset [] + environment: !override + POSTGRES_PASSWORD: ${DB_PASSWORD-postgres} + POSTGRES_USER: ${DB_USERNAME-postgres} + POSTGRES_DB: ${DB_DATABASE_NAME-immich} + POSTGRES_INITDB_ARGS: '--data-checksums' + volumes: + - ${UPLOAD_LOCATION-./Library}/postgres:/var/lib/postgresql/data + + redis: + env_file: !reset [] + +volumes: + # Node modules for each service to avoid conflicts and ensure consistent dependencies + cli_node_modules: + e2e_node_modules: + open_api_node_modules: + server_node_modules: + web_node_modules: diff --git a/.devcontainer/server/container-start-backend.sh b/.devcontainer/server/container-start-backend.sh new file mode 100755 index 0000000000..230a1378a2 --- /dev/null +++ b/.devcontainer/server/container-start-backend.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# shellcheck source=common.sh +# shellcheck disable=SC1091 +source /immich-devcontainer/container-common.sh + +echo "Starting Nest API Server" + +cd "${IMMICH_WORKSPACE}/server" || ( + echo workspace not found + exit 1 +) + +while true; do + node ./node_modules/.bin/nest start --debug "0.0.0.0:9230" --watch + echo " Nest API Server crashed with exit code $?. Respawning in 3s ..." + sleep 3 +done diff --git a/.devcontainer/server/container-start-frontend.sh b/.devcontainer/server/container-start-frontend.sh new file mode 100755 index 0000000000..43bde2a344 --- /dev/null +++ b/.devcontainer/server/container-start-frontend.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# shellcheck source=common.sh +# shellcheck disable=SC1091 +source /immich-devcontainer/container-common.sh + +echo "Starting Immich Web Frontend" + +cd "${IMMICH_WORKSPACE}/web" || ( + echo Workspace not found + exit 1 +) + +until curl --output /dev/null --silent --head --fail "http://127.0.0.1:${IMMICH_PORT}/api/server/config"; do + echo 'waiting for api server...' + sleep 1 +done + +while true; do + node ./node_modules/.bin/vite dev --host 0.0.0.0 --port "${DEV_PORT}" + echo "Web crashed with exit code $?. Respawning in 3s ..." + sleep 3 +done diff --git a/.devcontainer/server/container-start.sh b/.devcontainer/server/container-start.sh new file mode 100755 index 0000000000..ef22db5d72 --- /dev/null +++ b/.devcontainer/server/container-start.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# shellcheck source=common.sh +# shellcheck disable=SC1091 +source /immich-devcontainer/container-common.sh + +fix_permissions +install_dependencies diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000..119d6a961b --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,72 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Fix Permissions, Install Dependencies", + "type": "shell", + "command": "[ -f /immich-devcontainer/container-start.sh ] && /immich-devcontainer/container-start.sh || exit 0", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "dedicated", + "showReuseMessage": true, + "clear": false, + "group": "Devcontainer tasks", + "close": true + }, + "runOptions": { + "runOn": "default" + }, + "problemMatcher": [] + }, + { + "label": "Immich API Server (Nest)", + "dependsOn": ["Fix Permissions, Install Dependencies"], + "type": "shell", + "command": "[ -f /immich-devcontainer/container-start-backend.sh ] && /immich-devcontainer/container-start-backend.sh || exit 0", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "dedicated", + "showReuseMessage": true, + "clear": false, + "group": "Devcontainer tasks", + "close": true + }, + "runOptions": { + "runOn": "default" + }, + "problemMatcher": [] + }, + { + "label": "Immich Web Server (Vite)", + "dependsOn": ["Fix Permissions, Install Dependencies"], + "type": "shell", + "command": "[ -f /immich-devcontainer/container-start-frontend.sh ] && /immich-devcontainer/container-start-frontend.sh || exit 0", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "dedicated", + "showReuseMessage": true, + "clear": false, + "group": "Devcontainer tasks", + "close": true + }, + "runOptions": { + "runOn": "default" + }, + "problemMatcher": [] + }, + { + "label": "Immich Server and Web", + "dependsOn": ["Immich Web Server (Vite)", "Immich API Server (Nest)"], + "runOptions": { + "runOn": "folderOpen" + }, + "problemMatcher": [] + } + ] +} diff --git a/docs/docs/developer/devcontainers.md b/docs/docs/developer/devcontainers.md new file mode 100644 index 0000000000..10cb733383 --- /dev/null +++ b/docs/docs/developer/devcontainers.md @@ -0,0 +1,481 @@ +--- +title: Devcontainers +sidebar_position: 3 +--- + +# Development with Dev Containers + +Dev Containers provide a consistent, reproducible development environment using Docker containers. With a single click, you can get started with an Immich development environment on Mac, Linux, Windows, or in the cloud using GitHub Codespaces. + +[![Open in VSCode Containers](https://img.shields.io/static/v1?label=VSCode%20DevContainer&message=Immich&color=blue)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/immich-app/immich/) + +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/immich-app/immich/) + +[Learn more about Dev Containers](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers) + +## Prerequisites + +Before getting started, ensure you have: + +- **Docker Desktop** (latest version) + - [Mac](https://docs.docker.com/desktop/install/mac-install/) + - [Windows](https://docs.docker.com/desktop/install/windows-install/) (with WSL2 backend recommended) + - [Linux](https://docs.docker.com/desktop/install/linux-install/) +- **Visual Studio Code** with the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) +- **Git** for cloning the repository +- At least **8GB of RAM** (16GB recommended) +- **20GB of free disk space** + +:::tip Alternative Development Environments +While this guide focuses on VS Code, you have many options for Dev Container development: + +**Local Editors:** + +- [IntelliJ IDEA](https://www.jetbrains.com/help/idea/connect-to-devcontainer.html) - Full JetBrains IDE support +- [neovim](https://github.com/jamestthompson3/nvim-remote-containers) - Lightweight terminal-based editor +- [Emacs](https://github.com/emacs-lsp/lsp-docker) - Extensible text editor +- [DevContainer CLI](https://github.com/devcontainers/cli) - Command-line interface + +**Cloud-Based Solutions:** + +- [GitHub Codespaces](https://github.com/features/codespaces) - Fully integrated with GitHub, excellent devcontainer.json support +- [GitPod](https://www.gitpod.io) - SaaS platform with recent Dev Container support (historically used gitpod.yml) + +**Self-Hostable Options:** + +- [Coder](https://coder.com) - Enterprise-focused, requires Terraform knowledge, self-managed +- [DevPod](https://devpod.sh) - Client-only tool with excellent devcontainer.json support, works with any provider (local, cloud, or on-premise) + ::: + +## Dev Container Services + +The Dev Container environment consists of the following services: + +| Service | Container Name | Description | Ports | +| ---------------- | ------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------- | +| Server & Web | `immich-server` | Runs both API server and web frontend in development mode | 2283 (API)
3000 (Web)
9230 (Workers Debug)
9231 (API Debug) | +| Database | `database` | PostgreSQL database | 5432 | +| Cache | `redis` | Valkey cache server | 6379 | +| Machine Learning | `immich-machine-learning` | Immich ML model inference server | 3003 | + +## Getting Started + +### Step 1: Clone the Repository + +```bash +git clone https://github.com/immich-app/immich.git +cd immich +``` + +### Step 2: Configure Environment Variables + +The immich dev containers read environment variables from your shell environment, not from `.env` files. This allows them to work in cloud environments without pre-configuration. + +:::important Required Configuration +When running locally, and if you want to create (or use an existing) DB and/or photo storage folder, you must set the `UPLOAD_LOCATION` variable in your shell environment before launching the Dev Container. This determines where uploaded files are stored and also where the DB stores it data. + +```bash +# Set temporarily for current session +export UPLOAD_LOCATION=/opt/dev_upload_folder + +# Or add to your shell profile for persistence +# (~/.bashrc, ~/.zshrc, ~/.bash_profile, etc.) +echo 'export UPLOAD_LOCATION=/opt/dev_upload_folder' >> ~/.bashrc +source ~/.bashrc +``` + +::: + +### Step 3: Launch the Dev Container + +#### Using VS Code UI: + +1. Open the cloned repository in VS Code +2. Press `F1` or `Ctrl/Cmd+Shift+P` to open the command palette +3. Type and select "Dev Containers: Rebuild and Reopen in Container" +4. Select "Immich - Backend, Frontend and ML" from the list +5. Wait for the container to build and start (this may take several minutes on first run) + +#### Using VS Code Quick Actions: + +1. Open the repository in VS Code +2. You should see a popup asking if you want to reopen in a container +3. Click "Reopen in Container" + +#### Using Command Line: + +```bash +# Using the DevContainer CLI +devcontainer up --workspace-folder . +``` + +## Environment Variable Details + +### How Dev Containers Handle Environment Variables + +Unlike the Immich developer setup based on Docker Compose which uses `.env` files, Immich Dev Containers read environment variables from your shell environment. This is configured in `.devcontainer/devcontainer.json`: + +```json +"remoteEnv": { + "UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}", + "DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}", + "DB_USERNAME": "${localEnv:DB_USERNAME:postgres}", + "DB_DATABASE_NAME": "${localEnv:DB_DATABASE_NAME:immich}" +} +``` + +The `${localEnv:VARIABLE:default}` syntax reads from your shell environment with optional defaults. + +### Upload Location Path Resolution + +The `UPLOAD_LOCATION` environment variable controls where files are stored: + +**Default:** `./Library` (relative to the `docker` directory) +**Resolved to:** `/docker/Library` + +**Bind Mounts Created:** + +```yaml +# From .devcontainer/server/container-compose-overrides.yml +- ${UPLOAD_LOCATION-./Library}/photos:/workspaces/immich/server/upload +- ${UPLOAD_LOCATION-./Library}/postgres:/var/lib/postgresql/data +``` + +### Database Configuration + +These variables have sensible defaults (for development) but can be customized: + +| Variable | Default | Description | +| ------------------ | ---------- | ------------------- | +| `DB_PASSWORD` | `postgres` | PostgreSQL password | +| `DB_USERNAME` | `postgres` | PostgreSQL username | +| `DB_DATABASE_NAME` | `immich` | Database name | + +### Setting Environment Variables + +Add these to your shell profile (`~/.bashrc`, `~/.zshrc`, `~/.bash_profile`, etc.): + +```bash +# Required +export UPLOAD_LOCATION=./Library # or absolute path + +# Optional (only if using non-default values) +export DB_PASSWORD=your_password +export DB_USERNAME=your_username +export DB_DATABASE_NAME=your_database +``` + +Remember to reload your shell configuration: + +```bash +source ~/.bashrc # or ~/.zshrc, etc. +``` + +## Git Configuration + +### SSH Keys and Authentication + +To use your SSH keys for GitHub access inside the Dev Container: + +1. **Start SSH Agent** on your host machine: + + ```bash + eval "$(ssh-agent -s)" + ssh-add ~/.ssh/id_rsa # or your key path + ``` + +2. **VS Code automatically forwards your SSH agent** to the container + +For detailed instructions, see the [VS Code guide on sharing Git credentials](https://code.visualstudio.com/remote/advancedcontainers/sharing-git-credentials). + +### Commit Signing + +To use your SSH key for commit signing, see the [GitHub guide on SSH commit signing](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key#telling-git-about-your-ssh-key). + +## Development Workflow + +### Automatic Setup + +When the Dev Container starts, it automatically: + +1. **Runs post-create script** (`container-server-post-create.sh`): + + - Adjusts file permissions for the `node` user + - Installs dependencies: `npm install` in all packages + - Builds TypeScript SDK: `npm run build` in `open-api/typescript-sdk` + +2. **Starts development servers** via VS Code tasks: + + - `Immich API Server (Nest)` - API server with hot-reloading on port 2283 + - `Immich Web Server (Vite)` - Web frontend with hot-reloading on port 3000 + - Both servers watch for file changes and recompile automatically + +3. **Configures port forwarding**: + - Web UI: http://localhost:3000 (opens automatically) + - API: http://localhost:2283 + - Debug ports: 9230 (workers), 9231 (API) + +:::info +The Dev Container setup replaces the `make dev` command from the traditional setup. All services start automatically when you open the container. +::: + +### Accessing Services + +Once running, you can access: + +| Service | URL | Description | +| -------- | --------------------- | ---------------------------------------------------------------------------------------------- | +| Web UI | http://localhost:3000 | Main web interface | +| API | http://localhost:2283 | REST API endpoints (Not used directly, web UI will expose this over http://localhost:3000/api) | +| Database | localhost:5432 | PostgreSQL (username: `postgres`) (Not used directly) | + +### Connecting Mobile Apps + +To connect the mobile app to your Dev Container: + +1. Find your machine's IP address +2. In the mobile app, use: `http://YOUR_IP:3000/api` +3. Ensure your firewall allows connections on port 2283 + +### Making Code Changes + +- **Server code** (`/server`): Changes trigger automatic restart +- **Web code** (`/web`): Changes trigger hot module replacement +- **Database migrations**: Run `npm run sync:sql` in the server directory +- **API changes**: Regenerate TypeScript SDK with `make open-api` + +## Testing + +### Running Tests + +The Dev Container supports multiple ways to run tests: + +#### Using Make Commands (Recommended) + +```bash +# Run tests for specific components +make test-server # Server unit tests +make test-web # Web unit tests +make test-e2e # End-to-end tests +make test-cli # CLI tests + +# Run all tests +make test-all # Runs tests for all components + +# Medium tests (integration tests) +make test-medium-dev # End-to-end tests +``` + +#### Using NPM Directly + +```bash +# Server tests +cd /workspaces/immich/server +npm test # Run all tests +npm run test:watch # Watch mode +npm run test:cov # Coverage report + +# Web tests +cd /workspaces/immich/web +npm test # Run all tests +npm run test:watch # Watch mode + +# E2E tests +cd /workspaces/immich/e2e +npm run test # Run API tests +npm run test:web # Run web UI tests +``` + +### Code Quality Commands + +```bash +# Linting +make lint-server # Lint server code +make lint-web # Lint web code +make lint-all # Lint all components + +# Formatting +make format-server # Format server code +make format-web # Format web code +make format-all # Format all code + +# Type checking +make check-server # Type check server +make check-web # Type check web +make check-all # Check all components + +# Complete hygiene check +make hygiene-all # Runs lint, format, check, SQL sync, and audit +``` + +### Additional Make Commands + +```bash +# Build commands +make build-server # Build server +make build-web # Build web app +make build-all # Build everything + +# API generation +make open-api # Generate OpenAPI specs +make open-api-typescript # Generate TypeScript SDK +make open-api-dart # Generate Dart SDK + +# Database +make sql # Sync database schema + +# Dependencies +make install-server # Install server dependencies +make install-web # Install web dependencies +make install-all # Install all dependencies +``` + +### Debugging + +The Dev Container is pre-configured for debugging: + +1. **API Server Debugging**: + + - Set breakpoints in VS Code + - Press `F5` or use "Run and Debug" panel + - Select "Attach to Server" configuration + - Debug port: 9231 + +2. **Worker Debugging**: + + - Use "Attach to Workers" configuration + - Debug port: 9230 + +3. **Web Debugging**: + - Use browser DevTools + - VS Code debugger for Chrome/Edge extensions supported + +## Troubleshooting + +### Common Issues + +#### Permission Errors + +**Problem**: `EACCES` or permission denied errors +**Solution**: + +- The Dev Container runs as the `node` user (UID 1000) +- If your host UID differs, you may see permission issues +- Try rebuilding the container: "Dev Containers: Rebuild Container" + +#### Container Won't Start + +**Problem**: Dev Container fails to start or build +**Solution**: + +1. Check Docker is running: `docker ps` +2. Clean Docker resources: `docker system prune -a` +3. Check available disk space +4. Review Docker Desktop resource limits + +#### Port Already in Use + +**Problem**: "Port 3000/2283 is already in use" +**Solution**: + +1. Check for conflicting services: `lsof -i :3000` (macOS/Linux) +2. Stop conflicting services or change port mappings +3. Restart Docker Desktop + +#### Upload Location Not Set + +**Problem**: Errors about missing UPLOAD_LOCATION +**Solution**: + +1. Set the environment variable: `export UPLOAD_LOCATION=./Library` +2. Add to your shell profile for persistence +3. Restart your terminal and VS Code + +#### Database Connection Failed + +**Problem**: Cannot connect to PostgreSQL +**Solution**: + +1. Ensure all containers are running: `docker ps` +2. Check logs: "Dev Containers: Show Container Log" +3. Verify database credentials match environment variables + +### Getting Help + +If you encounter issues: + +1. Check container logs: View → Output → Select "Dev Containers" +2. Rebuild without cache: "Dev Containers: Rebuild Container Without Cache" +3. Review [common Docker issues](https://docs.docker.com/desktop/troubleshoot/) +4. Ask in [Discord](https://discord.immich.app) `#help-desk-support` channel + +## Mobile Development + +While the Dev Container focuses on server and web development, you can connect mobile apps for testing: + +### Connecting iOS/Android Apps + +1. **Ensure API is accessible**: + + ```bash + # Find your machine's IP + # macOS + ipconfig getifaddr en0 + # Linux + hostname -I + # Windows (in WSL2) + ip addr show eth0 + ``` + +2. **Configure mobile app**: + + - Server URL: `http://YOUR_IP:2283/api` + - Ensure firewall allows port 2283 + +3. **For full mobile development**, see the [mobile development guide](/docs/developer/setup) which covers: + - Flutter setup + - Running on simulators/devices + - Mobile-specific debugging + +## Advanced Configuration + +### Custom VS Code Extensions + +Add extensions to `.devcontainer/devcontainer.json`: + +```json +"customizations": { + "vscode": { + "extensions": [ + "your.extension-id" + ] + } +} +``` + +### Additional Services + +To add services (e.g., Redis Commander), modify: + +1. `/docker/docker-compose.dev.yml` - Add service definition +2. `/.devcontainer/server/container-compose-overrides.yml` - Add overrides if needed + +### Resource Limits + +Adjust Docker Desktop resources: + +- **macOS/Windows**: Docker Desktop → Settings → Resources +- **Linux**: Modify Docker daemon configuration + +Recommended minimums: + +- CPU: 4 cores +- Memory: 8GB +- Disk: 20GB + +## Next Steps + +- Read the [architecture overview](/docs/developer/architecture) +- Learn about [database migrations](/docs/developer/database-migrations) +- Explore [API documentation](/docs/api) +- Join `#immich` on [Discord](https://discord.immich.app) diff --git a/server/Dockerfile b/server/Dockerfile index fcf16ea139..21c2031227 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -16,6 +16,58 @@ ENV PATH="${PATH}:/usr/src/app/bin" \ NVIDIA_VISIBLE_DEVICES=all ENTRYPOINT ["tini", "--", "/bin/sh"] +FROM dev AS dev-container-server + +RUN apt-get update && \ + apt-get install sudo inetutils-ping openjdk-11-jre-headless \ + vim nano \ + -y --no-install-recommends --fix-missing + +RUN usermod -aG sudo node +RUN echo "node ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers +RUN mkdir -p /workspaces/immich +RUN chown node -R /workspaces + +RUN mkdir /immich-devcontainer && chown node -R /immich-devcontainer +COPY --chmod=777 ../.devcontainer/server/*.sh /immich-devcontainer/ + +FROM dev-container-server AS dev-container-mobile + +# Enable multiarch for arm64 if necessary +RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \ + dpkg --add-architecture amd64 && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + qemu-user-static \ + libc6:amd64 \ + libstdc++6:amd64 \ + libgcc1:amd64; \ + fi + +# Flutter SDK +# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux +ENV FLUTTER_CHANNEL="stable" +ENV FLUTTER_VERSION="3.29.3" +ENV FLUTTER_HOME=/flutter +ENV PATH=${PATH}:${FLUTTER_HOME}/bin + +# Flutter SDK +RUN mkdir -p ${FLUTTER_HOME} \ + && curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \ + && tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \ + && rm flutter.tar.xz \ + && chown -R node ${FLUTTER_HOME} + +USER node +RUN sudo apt-get update \ + && wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg \ + && echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list \ + && sudo apt-get update \ + && sudo apt-get install dcm -y + +COPY --chmod=777 ../.devcontainer/mobile/container-mobile-post-create.sh /immich-devcontainer/container-mobile-post-create.sh + +RUN dart --disable-analytics FROM dev AS prod