diff --git a/compose-files/databases/karakeep/docker-compose.yml b/compose-files/databases/karakeep/docker-compose.yml new file mode 100644 index 0000000..20a3cfc --- /dev/null +++ b/compose-files/databases/karakeep/docker-compose.yml @@ -0,0 +1,138 @@ +services: + # ========================================================================= + # Karakeep Web - Main application + # ========================================================================= + web: + image: ghcr.io/karakeep-app/karakeep:${KARAKEEP_VERSION:-release} + container_name: karakeep-web + hostname: karakeep-web + restart: unless-stopped + volumes: + - ./data:/data + ports: + - "3054:3000" + environment: + - DATA_DIR=/data + - MEILI_ADDR=http://meilisearch:7700 + - MEILI_MASTER_KEY=${MEILI_MASTER_KEY} + - BROWSER_WEB_URL=http://chrome:9222 + - OLLAMA_BASE_URL=http://ollama:11434 + - INFERENCE_TEXT_MODEL=${INFERENCE_TEXT_MODEL:-llama3.2:3b} + - INFERENCE_IMAGE_MODEL=${INFERENCE_IMAGE_MODEL:-llava} + - INFERENCE_LANG=${INFERENCE_LANG:-english} + - NEXTAUTH_URL=${NEXTAUTH_URL:-http://localhost:3000} + - NEXTAUTH_SECRET=${NEXTAUTH_SECRET} + - NODE_ENV=production + - NEXT_TELEMETRY_DISABLED=1 + depends_on: + meilisearch: + condition: service_started + chrome: + condition: service_started + ollama: + condition: service_started + networks: + - karakeep_internal + - proxy + deploy: + resources: + limits: + memory: 1G + cpus: '2.0' + healthcheck: + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://127.0.0.1:3000/api/health || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + labels: + - "autoheal=true" + - "com.centurylinklabs.watchtower.enable=true" + - "homepage.group=Personal" + - "homepage.name=Karakeep" + - "homepage.icon=karakeep.png" + - "homepage.href=https://karakeep.3ddbrewery.com" + + # ========================================================================= + # Meilisearch - Full-text search engine + # ========================================================================= + meilisearch: + image: getmeili/meilisearch:v1.13.3 + container_name: karakeep-meilisearch + hostname: meilisearch + restart: unless-stopped + volumes: + - ./meilisearch:/meili_data + ports: + - "7700:7700" + environment: + - MEILI_NO_ANALYTICS=true + - MEILI_MASTER_KEY=${MEILI_MASTER_KEY} + networks: + - karakeep_internal + deploy: + resources: + limits: + memory: 1G + cpus: '1.0' + labels: + - "autoheal=true" + - "com.centurylinklabs.watchtower.enable=true" + + # ========================================================================= + # Chrome - Headless browser for screenshots/crawling + # ========================================================================= + chrome: + image: gcr.io/zenika-hub/alpine-chrome:123 + container_name: karakeep-chrome + hostname: chrome + restart: unless-stopped + command: + - --no-sandbox + - --disable-gpu + - --disable-dev-shm-usage + - --remote-debugging-address=0.0.0.0 + - --remote-debugging-port=9222 + - --hide-scrollbars + networks: + - karakeep_internal + deploy: + resources: + limits: + memory: 512M + cpus: '1.0' + labels: + - "autoheal=true" + - "com.centurylinklabs.watchtower.enable=true" + + # ========================================================================= + # Ollama - Local AI inference (CPU-only on databases VM) + # ========================================================================= + ollama: + image: ollama/ollama:latest + container_name: karakeep-ollama + hostname: ollama + restart: unless-stopped + volumes: + - ./ollama:/root/.ollama + ports: + - "11434:11434" + environment: + - OLLAMA_HOST=0.0.0.0:11434 + networks: + - karakeep_internal + deploy: + resources: + limits: + # Ollama needs more memory for models (CPU-only, limited by VM) + memory: 4G + cpus: '2.0' + labels: + - "autoheal=true" + - "com.centurylinklabs.watchtower.enable=true" + +networks: + karakeep_internal: + driver: bridge + proxy: + external: true diff --git a/playbooks/deploy-karakeep.yml b/playbooks/deploy-karakeep.yml new file mode 100644 index 0000000..3bdf8f5 --- /dev/null +++ b/playbooks/deploy-karakeep.yml @@ -0,0 +1,134 @@ +--- +# Deploy Karakeep Stack to databases VM +# Containers: web, meilisearch, chrome, ollama +# Target: databases (192.168.1.81) + +- name: Deploy Karakeep Stack + hosts: databases + vars: + appdata_path: /home/docker/appdata + service_name: karakeep + service_dir: "{{ appdata_path }}/{{ service_name }}" + compose_src: "{{ playbook_dir }}/../compose-files/databases/{{ service_name }}" + + tasks: + # ========================================================================= + # PRE-FLIGHT CHECKS + # ========================================================================= + - name: Check if .env file exists on control server + delegate_to: localhost + ansible.builtin.stat: + path: "{{ compose_src }}/.env" + register: env_file + + - name: Fail if .env is missing + ansible.builtin.fail: + msg: | + .env file not found at {{ compose_src }}/.env + Copy .env.example to .env and fill in the secrets! + when: not env_file.stat.exists + + # ========================================================================= + # DIRECTORY SETUP + # ========================================================================= + - name: Create karakeep base directory + ansible.builtin.file: + path: "{{ service_dir }}" + state: directory + mode: '0755' + + - name: Create karakeep data directory + ansible.builtin.file: + path: "{{ service_dir }}/data" + state: directory + mode: '0755' + + - name: Create meilisearch directory + ansible.builtin.file: + path: "{{ service_dir }}/meilisearch" + state: directory + mode: '0755' + + - name: Create ollama directory + ansible.builtin.file: + path: "{{ service_dir }}/ollama" + state: directory + mode: '0755' + + # ========================================================================= + # FILE DEPLOYMENT + # ========================================================================= + - name: Copy docker-compose.yml + ansible.builtin.copy: + src: "{{ compose_src }}/docker-compose.yml" + dest: "{{ service_dir }}/docker-compose.yml" + mode: '0644' + + - name: Copy .env file + ansible.builtin.copy: + src: "{{ compose_src }}/.env" + dest: "{{ service_dir }}/.env" + mode: '0600' + + # ========================================================================= + # CONTAINER DEPLOYMENT + # ========================================================================= + - name: Deploy karakeep stack + community.docker.docker_compose_v2: + project_src: "{{ service_dir }}" + state: present + pull: always + register: karakeep_result + + - name: Show deployment status + ansible.builtin.debug: + msg: "Karakeep stack deployed: {{ karakeep_result.changed }}" + + # ========================================================================= + # VERIFICATION + # ========================================================================= + - name: Wait for karakeep-web to be healthy + ansible.builtin.uri: + url: "http://localhost:3054/api/health" + status_code: 200 + timeout: 10 + register: web_health + retries: 15 + delay: 10 + until: web_health.status == 200 + ignore_errors: true + + - name: Wait for meilisearch to be ready + ansible.builtin.uri: + url: "http://localhost:7700/health" + status_code: 200 + timeout: 5 + register: meili_health + retries: 10 + delay: 5 + until: meili_health.status == 200 + ignore_errors: true + + - name: Wait for ollama to be ready + ansible.builtin.uri: + url: "http://localhost:11434/api/version" + status_code: 200 + timeout: 5 + register: ollama_health + retries: 10 + delay: 5 + until: ollama_health.status == 200 + ignore_errors: true + + - name: Summary + ansible.builtin.debug: + msg: + - "=========================================" + - "Karakeep Stack Deployment Complete" + - "=========================================" + - "✅ Karakeep Web: http://192.168.1.81:3054" + - "✅ Meilisearch: http://192.168.1.81:7700" + - "✅ Ollama: http://192.168.1.81:11434" + - "=========================================" + - "NOTE: Ollama running on CPU only (no GPU)" + - "Models may need to be re-pulled after migration"