Last active
October 26, 2026 02:28
-
-
Save palaniraja/7d293297d7fead35603b13f92a5b8a85 to your computer and use it in GitHub Desktop.
Gemini screwed up: initial commits create/formats the enc diskimage on start instead of just setup
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| .DEFAULT_GOAL := help | |
| help: | |
| @echo "==================================================================" | |
| @echo " Immich Vault & Stack Management Makefile" | |
| @echo "==================================================================" | |
| @echo "Available Targets:" | |
| @echo " make start - Unlock vault, mount filesystem, and start Docker stack" | |
| @echo " make stop - Stop Docker stack, unmount, and lock encrypted vault" | |
| @echo " make status - Check Docker container status and vault mount state" | |
| @echo " make resize - Expand encrypted vault (Usage: make resize +20G)" | |
| @echo " make setup - [ONE-TIME] Initialize brand new encrypted vault image" | |
| @echo "==================================================================" | |
| -include .env | |
| export | |
| # --- CRITICAL SAFETY GUARD: Ensure required .env variables exist --- | |
| ifndef ENCRYPTED_IMAGE | |
| $(error ERROR: ENCRYPTED_IMAGE is not defined. Please check your .env file.) | |
| endif | |
| ifndef MOUNT_DIR | |
| $(error ERROR: MOUNT_DIR is not defined. Please check your .env file.) | |
| endif | |
| ifndef UPLOAD_LOCATION | |
| $(error ERROR: UPLOAD_LOCATION is not defined. Please check your .env file.) | |
| endif | |
| ifndef DB_DATA_LOCATION | |
| $(error ERROR: DB_DATA_LOCATION is not defined. Please check your .env file.) | |
| endif | |
| # Default vault size in MB if not defined in .env (50000 MB = ~50GB) | |
| VAULT_SIZE ?= 50000 | |
| # Fixed secure mapper target name | |
| FIXED_MAPPER_NAME = immich_crypt | |
| # Safely capture the actual host user's UID/GID, even if 'sudo make' was invoked | |
| HOST_UID := $(shell if [ -n "$$SUDO_UID" ]; then echo $$SUDO_UID; else id -u; fi) | |
| HOST_GID := $(shell if [ -n "$$SUDO_GID" ]; then echo $$SUDO_GID; else id -g; fi) | |
| .PHONY: setup start stop status resize | |
| setup: | |
| @if [ -f $(ENCRYPTED_IMAGE) ]; then \ | |
| echo "=================================================================="; \ | |
| echo " FATAL ERROR: Encrypted vault already exists at $(ENCRYPTED_IMAGE)."; \ | |
| echo "=================================================================="; \ | |
| echo " 'make setup' is strictly for brand-new installations."; \ | |
| echo " Running it again would overwrite and destroy your existing data."; \ | |
| echo "=================================================================="; \ | |
| exit 1; \ | |
| fi | |
| @echo "==> Creating parent directories..." | |
| sudo mkdir -p $$(dirname $(ENCRYPTED_IMAGE)) | |
| @echo "==> Creating $(VAULT_SIZE)MB encrypted vault file..." | |
| sudo dd if=/dev/zero of=$(ENCRYPTED_IMAGE) bs=1M count=$(VAULT_SIZE) status=progress | |
| @echo "==> Formatting LUKS container..." | |
| sudo cryptsetup luksFormat $(ENCRYPTED_IMAGE) | |
| sudo cryptsetup luksOpen $(ENCRYPTED_IMAGE) $(FIXED_MAPPER_NAME) | |
| sudo mkfs.ext4 -F /dev/mapper/$(FIXED_MAPPER_NAME) | |
| sudo cryptsetup luksClose $(FIXED_MAPPER_NAME) | |
| @echo "==> Setup complete! You can now run 'make start'." | |
| start: | |
| @if [ ! -b /dev/mapper/$(FIXED_MAPPER_NAME) ]; then \ | |
| echo "==> Unlocking encrypted vault..."; \ | |
| sudo cryptsetup luksOpen $(ENCRYPTED_IMAGE) $(FIXED_MAPPER_NAME); \ | |
| fi | |
| @if ! mountpoint -q $(MOUNT_DIR); then \ | |
| if [ ! -d $(MOUNT_DIR) ]; then sudo mkdir -p $(MOUNT_DIR); fi; \ | |
| echo "==> Mounting filesystem..."; \ | |
| sudo mount /dev/mapper/$(FIXED_MAPPER_NAME) $(MOUNT_DIR); \ | |
| fi | |
| @echo "==> Verifying permissions using host UID ($(HOST_UID):$(HOST_GID))..." | |
| sudo mkdir -p $(UPLOAD_LOCATION) $(DB_DATA_LOCATION) | |
| sudo chown -R $(HOST_UID):$(HOST_GID) $(UPLOAD_LOCATION) | |
| sudo chown -R 70:70 $(DB_DATA_LOCATION) | |
| sudo chmod 700 $(DB_DATA_LOCATION) | |
| @echo "==> Starting Immich..." | |
| docker compose up -d | |
| stop: | |
| @echo "==> Stopping Immich containers..." | |
| docker compose down | |
| @echo "==> Waiting for file handles to release..." | |
| @if mountpoint -q $(MOUNT_DIR); then \ | |
| echo "==> Unmounting directory with retry loop..."; \ | |
| RETRIES=10; \ | |
| while [ $$RETRIES -gt 0 ]; do \ | |
| if sudo umount $(MOUNT_DIR) 2>/dev/null; then \ | |
| echo "==> Unmounted successfully."; \ | |
| break; \ | |
| fi; \ | |
| RETRIES=$$((RETRIES - 1)); \ | |
| if [ $$RETRIES -eq 0 ]; then \ | |
| echo "=================================================================="; \ | |
| echo " ERROR: Failed to unmount $(MOUNT_DIR) after 10 attempts."; \ | |
| echo "=================================================================="; \ | |
| echo " TIP: Check if any open terminal session has its current directory (pwd)"; \ | |
| echo " set inside $(MOUNT_DIR). If so, 'cd ~' out of it."; \ | |
| echo ""; \ | |
| echo " Processes currently locking files:"; \ | |
| if command -v lsof >/dev/null 2>&1; then \ | |
| sudo lsof +D $(MOUNT_DIR) || echo " (No active file handles detected by lsof)"; \ | |
| else \ | |
| echo " (lsof utility not installed)"; \ | |
| fi; \ | |
| echo "=================================================================="; \ | |
| exit 1; \ | |
| fi; \ | |
| sleep 1; \ | |
| done; \ | |
| fi | |
| @if [ -b /dev/mapper/$(FIXED_MAPPER_NAME) ]; then \ | |
| echo "==> Locking vault..."; \ | |
| sudo cryptsetup luksClose $(FIXED_MAPPER_NAME); \ | |
| fi | |
| @echo "==> Vault successfully secured and locked." | |
| status: | |
| @echo "=== Docker Status ===" | |
| docker compose ps | |
| @echo "\n=== Mount & Vault Status ===" | |
| @mountpoint -q $(MOUNT_DIR) && echo "Filesystem: MOUNTED ($(MOUNT_DIR))" || echo "Filesystem: UNMOUNTED" | |
| @if [ -b /dev/mapper/$(FIXED_MAPPER_NAME) ]; then echo "LUKS Vault: UNLOCKED"; else echo "LUKS Vault: LOCKED"; fi | |
| # If the first goal is resize, catch any extra arguments (like +20G) so Make doesn't error out | |
| ifeq (resize,$(firstword $(MAKECMDGOALS))) | |
| RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) | |
| $(eval $(RUN_ARGS):;@:) | |
| endif | |
| resize: | |
| @if [ -z "$(RUN_ARGS)" ]; then \ | |
| echo "=================================================================="; \ | |
| echo " ERROR: Missing size argument for resize."; \ | |
| echo "=================================================================="; \ | |
| echo "Usage:"; \ | |
| echo " make resize +20G (to expand by 20 Gigabytes)"; \ | |
| echo " make resize +500M (to expand by 500 Megabytes)"; \ | |
| echo ""; \ | |
| echo "Current Vault Image: $(ENCRYPTED_IMAGE)"; \ | |
| echo "Host Disk Space Available:"; \ | |
| df -h $$(dirname $(ENCRYPTED_IMAGE)) | awk 'NR==1 || NR==2 {print $$0}'; \ | |
| echo "=================================================================="; \ | |
| exit 1; \ | |
| fi | |
| @# Guard against dangerous negative arguments that would destroy data | |
| @case "$(RUN_ARGS)" in \ | |
| -+*) ;; \ | |
| +*) ;; \ | |
| *) \ | |
| echo "=================================================================="; \ | |
| echo " ERROR: Invalid resize argument '$(RUN_ARGS)'."; \ | |
| echo " Shrinking volumes via Makefile is disabled to prevent data loss."; \ | |
| echo " Please use a positive expansion format, e.g., 'make resize +20G'"; \ | |
| echo "=================================================================="; \ | |
| exit 1 ;; \ | |
| esac | |
| @echo "==> [1/6] Running pre-flight checks for expansion target: $(RUN_ARGS)..." | |
| @if [ ! -f $(ENCRYPTED_IMAGE) ]; then \ | |
| echo "Error: Encrypted image file not found at $(ENCRYPTED_IMAGE). Run 'make setup' first."; \ | |
| exit 1; \ | |
| fi | |
| @echo "" | |
| @echo "=== Current Host Disk Usage ===" | |
| @df -h $$(dirname $(ENCRYPTED_IMAGE)) | |
| @echo "" | |
| @echo "=== Current Vault / Filesystem Usage (if mounted) ===" | |
| @if mountpoint -q $(MOUNT_DIR); then \ | |
| df -h $(MOUNT_DIR); \ | |
| else \ | |
| echo "Vault is currently unmounted (will check after unlocking)."; \ | |
| fi | |
| @echo "" | |
| @read -p "==> Proceed with expanding container by $(RUN_ARGS)? [y/N] " confirm && [ "$$confirm" = "y" ] || { echo "Aborted."; exit 1; } | |
| @echo "==> [2/6] Ensuring stack is fully stopped and unmounted safely..." | |
| @$(MAKE) stop | |
| @echo "==> [3/6] Expanding underlying image file ($(ENCRYPTED_IMAGE)) by $(RUN_ARGS)..." | |
| sudo truncate -s $(RUN_ARGS) $(ENCRYPTED_IMAGE) | |
| @echo "==> [4/6] Unlocking LUKS container..." | |
| sudo cryptsetup luksOpen $(ENCRYPTED_IMAGE) $(FIXED_MAPPER_NAME) | |
| @echo "==> [5/6] Resizing LUKS mapping layer..." | |
| sudo cryptsetup resize $(FIXED_MAPPER_NAME) | |
| @echo "==> [6/6] Temporarily mounting and resizing filesystem to claim space..." | |
| sudo mkdir -p $(MOUNT_DIR) | |
| sudo mount /dev/mapper/$(FIXED_MAPPER_NAME) $(MOUNT_DIR) | |
| sudo resize2fs /dev/mapper/$(FIXED_MAPPER_NAME) | |
| sudo umount $(MOUNT_DIR) | |
| sudo cryptsetup luksClose $(FIXED_MAPPER_NAME) | |
| @echo "==> Expansion operation completed successfully! Run 'make start' to boot Immich." |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
After Gemini screwed up my
starton initial commit resulting in me loosing data. I was experimenting by asking it with $100-$250 bounty on each iteration to fix defects.An exercise to never trust these results because first happy path succeeds. I still had to review all these again just to have a peace of mind is an example why token/budget based limits is not going to solve software development.