--- - name: Create paperclip directories ansible.builtin.file: path: "{{ item }}" state: directory mode: '0755' owner: root group: root loop: - "{{ paperclip_base_path }}" # The postgres image chowns its own data dir on start; do not chown it here. - "{{ paperclip_base_path }}/pgdata" become: true tags: [paperclip, files] # Container runs as UID 1000. Deliberately NOT recursive: recurse + mode would # chmod every file the app writes under these paths on each run. A fresh # deploy has no root-owned content to repair; if you ever need to, run a # one-off `chown -R 1000:1000 ` by hand. - name: Create container-owned directories (UID 1000) ansible.builtin.file: path: "{{ item }}" state: directory mode: '0755' owner: 1000 group: 1000 loop: - "{{ paperclip_data_dir }}" - "{{ paperclip_base_path }}/.config/opencode" - "{{ paperclip_base_path }}/.local/share/opencode" become: true tags: [paperclip, files] # - name: Deploy OpenCode auth.json with vLLM credentials # template: # src: opencode-auth.json.j2 # dest: "{{ paperclip_base_path }}/.local/share/opencode/auth.json" # mode: '0600' # owner: 1000 # group: 1000 # become: true # no_log: true # This file is both the compose interpolation source and the container's # env_file, so everything here is visible inside the Paperclip container. # Infrastructure secrets (Cloudflare token, certbot email) live in # /opt/nginx/.env instead. - name: Write secrets and configuration to .env ansible.builtin.copy: content: | # Authentication secrets (from vault) BETTER_AUTH_SECRET={{ vault_better_auth_secret }} PAPERCLIP_AGENT_JWT_SECRET={{ vault_paperclip_agent_jwt_secret }} # Inference endpoints ILLUMINATIONS_ENDPOINT={{ illuminations_endpoint }} ILLUMINATIONS_API_KEY={{ vault_illuminations_api_key }} FANTASMIC_ENDPOINT={{ fantasmic_endpoint }} FANTASMIC_API_KEY={{ vault_fantasmic_api_key }} dest: "{{ paperclip_base_path }}/.env" mode: '0600' owner: root group: root become: true no_log: true notify: Restart paperclip stack tags: [paperclip, files] # No notify: the deploy task below recreates whatever the compose file changed. - name: Template docker-compose.yml ansible.builtin.template: src: docker-compose.yml.j2 dest: "{{ paperclip_base_path }}/docker-compose.yml" mode: '0644' owner: root group: root become: true tags: [paperclip, files] # - name: Template OpenCode config # ansible.builtin.template: # src: opencode.jsonc.j2 # dest: "{{ paperclip_base_path }}/.config/opencode/opencode.jsonc" # mode: '0644' # owner: 1000 # group: 1000 # become: true # notify: Restart paperclip stack # tags: [paperclip, files] # Idempotent: only changed services are recreated. remove_orphans also cleans # up the old paperclip-nginx / paperclip-certbot containers if they still exist. - name: Deploy paperclip stack community.docker.docker_compose_v2: project_src: "{{ paperclip_base_path }}" pull: missing remove_orphans: true state: present register: paperclip_deploy become: true tags: [paperclip, deploy] - name: Wait for Paperclip to be ready ansible.builtin.uri: url: "http://localhost:{{ paperclip_port }}/health" status_code: 200 register: paperclip_health until: paperclip_health.status == 200 retries: 30 delay: 5 ignore_errors: true tags: [paperclip, deploy, verify] - name: Warn if Paperclip never became healthy ansible.builtin.debug: msg: "WARNING: Paperclip did not report healthy on port {{ paperclip_port }}. Check: docker logs paperclip" when: paperclip_health.status | default(0) != 200 tags: [paperclip, verify] # Requires day1_deploy_nginx_proxy.yml to have run first. - name: Publish Paperclip through nginx-proxy ansible.builtin.include_role: name: nginx-proxy tasks_from: add_vhost apply: become: true tags: [paperclip, nginx] vars: vhost_name: paperclip vhost_domain: "{{ paperclip_domain }}" vhost_upstream_port: "{{ paperclip_port }}" vhost_health_path: /health tags: [paperclip, nginx] # --resolve keeps this independent of DNS: it tests nginx, the cert and the # upstream. DNS is a separate (manual) prerequisite. - name: Verify HTTPS through nginx ansible.builtin.command: >- curl -sk -o /dev/null -w '%{http_code}' --resolve {{ paperclip_domain }}:443:127.0.0.1 https://{{ paperclip_domain }}/ register: paperclip_https until: paperclip_https.stdout in ['200', '301', '302'] retries: 6 delay: 5 changed_when: false failed_when: false tags: [paperclip, verify] - name: Report HTTPS result ansible.builtin.debug: msg: >- {{ 'Paperclip is serving at https://' ~ paperclip_domain if paperclip_https.stdout in ['200', '301', '302'] else 'WARNING: https://' ~ paperclip_domain ~ ' returned ' ~ (paperclip_https.stdout | default('no response')) ~ ' (502 usually means Paperclip is not up yet)' }} tags: [paperclip, verify]