Compare commits

..

2 Commits

Author SHA1 Message Date
343b0e13d6 fix(certbot): harden renewal hook and fix permission errors
The renewal deploy-hook ran as the certbot user but lacked permissions to
write the combined PEM to /etc/haproxy/certs and to reload HAProxy,
causing silent failures that left a stale certificate in production until
expiry.

- Add certbot user to the haproxy group so it can write the combined PEM
- Grant certbot NOPASSWD sudo for `systemctl reload haproxy` only
- Make the Prometheus textfile directory group-owned by certbot (0775)
  so cert-metrics.sh can atomically update ssl_cert.prom
- Refactor renewal-hook.sh to always refresh cert metrics on exit via a
  trap, ensuring expiry alerts fire when the hook itself is broken
- Replace `set -e` with explicit error handling and structured logging
2026-06-17 09:58:46 -04:00
2f5a15eef5 chore(haproxy,terraform): harden haproxy stats and pin incus provider
- Add maxconn limit and HTTP timeouts to mitigate slowloris attacks
- Restrict stats endpoint to internal LAN and localhost only
- Hide HAProxy version on stats page
- Pin Incus Terraform provider to ~> 1.0 for stability
2026-06-09 22:52:23 -04:00
12 changed files with 676 additions and 47 deletions

View File

@@ -86,6 +86,19 @@
groups: "{{ certbot_group }}" groups: "{{ certbot_group }}"
append: true append: true
# The renewal deploy-hook runs as the certbot user and writes the combined
# PEM into the group-writable /etc/haproxy/certs (mode 0770, owned by the
# haproxy group). certbot must be a member of that group, otherwise the
# hook fails with "Permission denied" and HAProxy serves a stale cert until
# it expires.
- name: Add certbot user to the haproxy group
become: true
ansible.builtin.user:
name: "{{ certbot_user }}"
groups: "{{ haproxy_group }}"
append: true
when: "'haproxy' in services | default([])"
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# Directory Structure # Directory Structure
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
@@ -178,14 +191,32 @@
group: "{{ certbot_group }}" group: "{{ certbot_group }}"
mode: '0750' mode: '0750'
# Group-owned by certbot and group-writable so cert-metrics.sh (run as the
# certbot user from the renewal hook) can atomically write ssl_cert.prom.
# node-exporter only needs to read these files, which 0775 still allows.
# The renewal hook reloads HAProxy after installing a new cert, but runs as
# the unprivileged certbot user. Grant exactly `systemctl reload haproxy`
# via sudo — nothing more. visudo validation prevents a malformed drop-in
# from locking out sudo.
- name: Allow certbot to reload HAProxy via sudo
become: true
ansible.builtin.copy:
dest: /etc/sudoers.d/certbot-haproxy-reload
content: "{{ certbot_user }} ALL=(root) NOPASSWD: /usr/bin/systemctl reload haproxy\n"
owner: root
group: root
mode: '0440'
validate: visudo -cf %s
when: "'haproxy' in services | default([])"
- name: Create Prometheus textfile directory - name: Create Prometheus textfile directory
become: true become: true
ansible.builtin.file: ansible.builtin.file:
path: "{{ prometheus_node_exporter_text_directory }}" path: "{{ prometheus_node_exporter_text_directory }}"
state: directory state: directory
owner: root owner: root
group: root group: "{{ certbot_group }}"
mode: '0755' mode: '0775'
- name: Template certificate metrics script - name: Template certificate metrics script
become: true become: true

View File

@@ -8,7 +8,7 @@
# 3. Reloads HAProxy via systemd # 3. Reloads HAProxy via systemd
# 4. Updates certificate metrics for Prometheus # 4. Updates certificate metrics for Prometheus
set -euo pipefail set -uo pipefail
# RENEWED_LINEAGE is set by certbot --deploy-hook or passed explicitly by deploy.yml # RENEWED_LINEAGE is set by certbot --deploy-hook or passed explicitly by deploy.yml
CERT_DIR="${RENEWED_LINEAGE:?RENEWED_LINEAGE must be set}" CERT_DIR="${RENEWED_LINEAGE:?RENEWED_LINEAGE must be set}"
@@ -16,37 +16,70 @@ CERT_NAME=$(basename "${CERT_DIR}")
HAPROXY_CERT="{{ haproxy_cert_path }}" HAPROXY_CERT="{{ haproxy_cert_path }}"
HAPROXY_DIR="{{ haproxy_directory }}" HAPROXY_DIR="{{ haproxy_directory }}"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting renewal hook for ${CERT_NAME}" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
fail() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $*" >&2; }
# Always refresh Prometheus cert metrics on exit, even if installation below
# fails. The metrics drive the SSLCertificateExpired/ExpiringSoon alerts, so
# they must reflect reality precisely when the hook is broken — otherwise a
# failed renewal rots silently (which is exactly how the cert expired before).
# A non-zero exit is reported by certbot as a WARNING, surfacing the failure.
hook_status=0
finish() {
{{ certbot_directory }}/hooks/cert-metrics.sh || fail "cert-metrics.sh failed"
if [[ ${hook_status} -ne 0 ]]; then
fail "Renewal hook FAILED for ${CERT_NAME} — HAProxy is serving a STALE certificate"
fi
exit "${hook_status}"
}
trap finish EXIT
log "Starting renewal hook for ${CERT_NAME}"
# Check if certificate files exist # Check if certificate files exist
if [[ ! -f "${CERT_DIR}/fullchain.pem" ]] || [[ ! -f "${CERT_DIR}/privkey.pem" ]]; then if [[ ! -f "${CERT_DIR}/fullchain.pem" ]] || [[ ! -f "${CERT_DIR}/privkey.pem" ]]; then
echo "ERROR: Certificate files not found in ${CERT_DIR}" fail "Certificate files not found in ${CERT_DIR}"
hook_status=1
exit 1 exit 1
fi fi
# Combine certificate and private key for HAProxy # Combine certificate and private key for HAProxy (single PEM), writing to a
# HAProxy requires both in a single PEM file # temp file in the same directory and moving atomically so HAProxy never reads
cat "${CERT_DIR}/fullchain.pem" "${CERT_DIR}/privkey.pem" > "${HAPROXY_CERT}.tmp" # a partial file. A permission failure here is the documented failure mode.
if ! cat "${CERT_DIR}/fullchain.pem" "${CERT_DIR}/privkey.pem" > "${HAPROXY_CERT}.tmp"; then
fail "Could not write ${HAPROXY_CERT}.tmp — check ownership/permissions of $(dirname "${HAPROXY_CERT}")"
rm -f "${HAPROXY_CERT}.tmp"
hook_status=1
exit 1
fi
# Atomic move to avoid HAProxy reading partial file if ! mv "${HAPROXY_CERT}.tmp" "${HAPROXY_CERT}"; then
mv "${HAPROXY_CERT}.tmp" "${HAPROXY_CERT}" fail "Could not move combined PEM into place at ${HAPROXY_CERT}"
rm -f "${HAPROXY_CERT}.tmp"
hook_status=1
exit 1
fi
# Set permissions # Set permissions
chown {{ certbot_user }}:{{ haproxy_group }} "${HAPROXY_CERT}" chown {{ certbot_user }}:{{ haproxy_group }} "${HAPROXY_CERT}"
chmod 640 "${HAPROXY_CERT}" chmod 640 "${HAPROXY_CERT}"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Certificate combined and written to ${HAPROXY_CERT}" log "Certificate combined and written to ${HAPROXY_CERT}"
# Reload HAProxy if running # Reload HAProxy if running. The hook runs as the unprivileged certbot user,
# so the reload goes through sudo (a scoped sudoers rule grants exactly this
# command). sudo -n fails fast rather than blocking on a password prompt.
if systemctl is-active --quiet haproxy; then if systemctl is-active --quiet haproxy; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Reloading HAProxy..." log "Reloading HAProxy..."
systemctl reload haproxy if sudo -n systemctl reload haproxy; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] HAProxy reloaded" log "HAProxy reloaded"
else
fail "HAProxy reload failed"
hook_status=1
exit 1
fi
else else
echo "[$(date '+%Y-%m-%d %H:%M:%S')] HAProxy not running, skipping reload" log "HAProxy not running, skipping reload"
fi fi
# Update certificate metrics log "Renewal hook completed successfully"
{{ certbot_directory }}/hooks/cert-metrics.sh
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Renewal hook completed successfully"

469
ansible/comfyui/README.md Normal file
View File

@@ -0,0 +1,469 @@
<div align="center">
# ComfyUI
**The most powerful and modular AI engine for content creation.**
[![Website][website-shield]][website-url]
[![Dynamic JSON Badge][discord-shield]][discord-url]
[![Twitter][twitter-shield]][twitter-url]
[![Matrix][matrix-shield]][matrix-url]
<br>
[![][github-release-shield]][github-release-link]
[![][github-release-date-shield]][github-release-link]
[![][github-downloads-shield]][github-downloads-link]
[![][github-downloads-latest-shield]][github-downloads-link]
[matrix-shield]: https://img.shields.io/badge/Matrix-000000?style=flat&logo=matrix&logoColor=white
[matrix-url]: https://app.element.io/#/room/%23comfyui_space%3Amatrix.org
[website-shield]: https://img.shields.io/badge/ComfyOrg-4285F4?style=flat
[website-url]: https://www.comfy.org/
<!-- Workaround to display total user from https://github.com/badges/shields/issues/4500#issuecomment-2060079995 -->
[discord-shield]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fdiscord.com%2Fapi%2Finvites%2Fcomfyorg%3Fwith_counts%3Dtrue&query=%24.approximate_member_count&logo=discord&logoColor=white&label=Discord&color=green&suffix=%20total
[discord-url]: https://discord.com/invite/comfyorg
[twitter-shield]: https://img.shields.io/twitter/follow/ComfyUI
[twitter-url]: https://x.com/ComfyUI
[github-release-shield]: https://img.shields.io/github/v/release/comfyanonymous/ComfyUI?style=flat&sort=semver
[github-release-link]: https://github.com/comfyanonymous/ComfyUI/releases
[github-release-date-shield]: https://img.shields.io/github/release-date/comfyanonymous/ComfyUI?style=flat
[github-downloads-shield]: https://img.shields.io/github/downloads/comfyanonymous/ComfyUI/total?style=flat
[github-downloads-latest-shield]: https://img.shields.io/github/downloads/comfyanonymous/ComfyUI/latest/total?style=flat&label=downloads%40latest
[github-downloads-link]: https://github.com/comfyanonymous/ComfyUI/releases
<img width="1590" height="795" alt="ComfyUI Screenshot" src="https://github.com/user-attachments/assets/36e065e0-bfae-4456-8c7f-8369d5ea48a2" />
<br>
</div>
ComfyUI is the AI creation engine for visual professionals who demand control over every model, every parameter, and every output. Its powerful and modular node graph interface empowers creatives to generate images, videos, 3D models, audio, and more...
- ComfyUI natively supports the latest open-source state of the art models.
- API nodes provide access to the best closed source models such as Nano Banana, Seedance, Hunyuan3D, etc.
- It is available on Windows, Linux, and macOS, locally with our [desktop application](https://www.comfy.org/download), our [portable install](#installing) or on our [cloud](https://www.comfy.org/cloud).
- The most sophisticated workflows can be exposed through a simple UI thanks to App Mode.
- It integrates seamlessly into production pipelines with our API endpoints.
## Get Started
### Local
#### [Desktop Application](https://www.comfy.org/download)
- The easiest way to get started.
- Available on Windows & macOS.
#### [Windows Portable Package](#installing)
- Get the latest commits and completely portable.
- Available on Windows.
#### [Manual Install](#manual-install-windows-linux)
Supports all operating systems and GPU types (NVIDIA, AMD, Intel, Apple Silicon, Ascend).
### Cloud
#### [Comfy Cloud](https://www.comfy.org/cloud)
- Our official paid cloud version for those who can't afford local hardware.
## Examples
See what ComfyUI can do with the [newer template workflows](https://comfy.org/workflows) or old [example workflows](https://comfyanonymous.github.io/ComfyUI_examples/).
## Features
- Nodes/graph/flowchart interface to experiment and create complex Stable Diffusion workflows without needing to code anything.
- NOTE: There are many more models supported than the list below, if you want to see what is supported see our templates list inside ComfyUI.
- Image Models
- SD1.x, SD2.x ([unCLIP](https://comfyanonymous.github.io/ComfyUI_examples/unclip/))
- [SDXL](https://comfyanonymous.github.io/ComfyUI_examples/sdxl/), [SDXL Turbo](https://comfyanonymous.github.io/ComfyUI_examples/sdturbo/)
- [Stable Cascade](https://comfyanonymous.github.io/ComfyUI_examples/stable_cascade/)
- [SD3 and SD3.5](https://comfyanonymous.github.io/ComfyUI_examples/sd3/)
- Pixart Alpha and Sigma
- [AuraFlow](https://comfyanonymous.github.io/ComfyUI_examples/aura_flow/)
- [HunyuanDiT](https://comfyanonymous.github.io/ComfyUI_examples/hunyuan_dit/)
- [Flux](https://comfyanonymous.github.io/ComfyUI_examples/flux/)
- [Lumina Image 2.0](https://comfyanonymous.github.io/ComfyUI_examples/lumina2/)
- [HiDream](https://comfyanonymous.github.io/ComfyUI_examples/hidream/)
- [Qwen Image](https://comfyanonymous.github.io/ComfyUI_examples/qwen_image/)
- [Hunyuan Image 2.1](https://comfyanonymous.github.io/ComfyUI_examples/hunyuan_image/)
- [Flux 2](https://comfyanonymous.github.io/ComfyUI_examples/flux2/)
- [Z Image](https://comfyanonymous.github.io/ComfyUI_examples/z_image/)
- Ernie Image
- Image Editing Models
- [Omnigen 2](https://comfyanonymous.github.io/ComfyUI_examples/omnigen/)
- [Flux Kontext](https://comfyanonymous.github.io/ComfyUI_examples/flux/#flux-kontext-image-editing-model)
- [HiDream E1.1](https://comfyanonymous.github.io/ComfyUI_examples/hidream/#hidream-e11)
- [Qwen Image Edit](https://comfyanonymous.github.io/ComfyUI_examples/qwen_image/#edit-model)
- Video Models
- [Stable Video Diffusion](https://comfyanonymous.github.io/ComfyUI_examples/video/)
- [Mochi](https://comfyanonymous.github.io/ComfyUI_examples/mochi/)
- [LTX-Video](https://comfyanonymous.github.io/ComfyUI_examples/ltxv/)
- [Hunyuan Video](https://comfyanonymous.github.io/ComfyUI_examples/hunyuan_video/)
- [Wan 2.1](https://comfyanonymous.github.io/ComfyUI_examples/wan/)
- [Wan 2.2](https://comfyanonymous.github.io/ComfyUI_examples/wan22/)
- [Hunyuan Video 1.5](https://docs.comfy.org/tutorials/video/hunyuan/hunyuan-video-1-5)
- Audio Models
- [Stable Audio](https://comfyanonymous.github.io/ComfyUI_examples/audio/)
- [ACE Step](https://comfyanonymous.github.io/ComfyUI_examples/audio/)
- 3D Models
- [Hunyuan3D 2.0](https://docs.comfy.org/tutorials/3d/hunyuan3D-2)
- Asynchronous Queue system
- Many optimizations: Only re-executes the parts of the workflow that changes between executions.
- Smart memory management: can automatically run large models on GPUs with as low as 1GB vram with smart offloading.
- Works even if you don't have a GPU with: ```--cpu``` (slow)
- Can load ckpt and safetensors: All in one checkpoints or standalone diffusion models, VAEs and CLIP models.
- Safe loading of ckpt, pt, pth, etc.. files.
- Embeddings/Textual inversion
- [Loras (regular, locon and loha)](https://comfyanonymous.github.io/ComfyUI_examples/lora/)
- [Hypernetworks](https://comfyanonymous.github.io/ComfyUI_examples/hypernetworks/)
- Loading full workflows (with seeds) from generated PNG, WebP and FLAC files.
- Saving/Loading workflows as Json files.
- Nodes interface can be used to create complex workflows like one for [Hires fix](https://comfyanonymous.github.io/ComfyUI_examples/2_pass_txt2img/) or much more advanced ones.
- [Area Composition](https://comfyanonymous.github.io/ComfyUI_examples/area_composition/)
- [Inpainting](https://comfyanonymous.github.io/ComfyUI_examples/inpaint/) with both regular and inpainting models.
- [ControlNet and T2I-Adapter](https://comfyanonymous.github.io/ComfyUI_examples/controlnet/)
- [Upscale Models (ESRGAN, ESRGAN variants, SwinIR, Swin2SR, etc...)](https://comfyanonymous.github.io/ComfyUI_examples/upscale_models/)
- [GLIGEN](https://comfyanonymous.github.io/ComfyUI_examples/gligen/)
- [Model Merging](https://comfyanonymous.github.io/ComfyUI_examples/model_merging/)
- [LCM models and Loras](https://comfyanonymous.github.io/ComfyUI_examples/lcm/)
- Latent previews with [TAESD](#how-to-show-high-quality-previews)
- Works fully offline: core will never download anything unless you want to.
- Optional API nodes to use paid models from external providers through the online [Comfy API](https://docs.comfy.org/tutorials/api-nodes/overview) disable with: `--disable-api-nodes`
- [Config file](extra_model_paths.yaml.example) to set the search paths for models.
Workflow examples can be found on the [Examples page](https://comfyanonymous.github.io/ComfyUI_examples/)
## Release Process
ComfyUI follows a weekly release cycle targeting Monday but this regularly changes because of model releases or large changes to the codebase. There are three interconnected repositories:
1. **[ComfyUI Core](https://github.com/comfyanonymous/ComfyUI)**
- Releases a new major stable version (e.g., v0.7.0) roughly every 2 weeks.
- Starting from v0.4.0 patch versions will be used for fixes backported onto the current stable release.
- Minor versions will be used for releases off the master branch.
- Patch versions may still be used for releases on the master branch in cases where a backport would not make sense.
- Commits outside of the stable release tags may be very unstable and break many custom nodes.
- Serves as the foundation for the desktop release
2. **[ComfyUI Desktop](https://github.com/Comfy-Org/desktop)**
- Builds a new release using the latest stable core version
3. **[ComfyUI Frontend](https://github.com/Comfy-Org/ComfyUI_frontend)**
- Every 2+ weeks frontend updates are merged into the core repository
- Features are frozen for the upcoming core release
- Development continues for the next release cycle
## Shortcuts
| Keybind | Explanation |
|------------------------------------|--------------------------------------------------------------------------------------------------------------------|
| `Ctrl` + `Enter` | Queue up current graph for generation |
| `Ctrl` + `Shift` + `Enter` | Queue up current graph as first for generation |
| `Ctrl` + `Alt` + `Enter` | Cancel current generation |
| `Ctrl` + `Z`/`Ctrl` + `Y` | Undo/Redo |
| `Ctrl` + `S` | Save workflow |
| `Ctrl` + `O` | Load workflow |
| `Ctrl` + `A` | Select all nodes |
| `Alt `+ `C` | Collapse/uncollapse selected nodes |
| `Ctrl` + `M` | Mute/unmute selected nodes |
| `Ctrl` + `B` | Bypass selected nodes (acts like the node was removed from the graph and the wires reconnected through) |
| `Delete`/`Backspace` | Delete selected nodes |
| `Ctrl` + `Backspace` | Delete the current graph |
| `Space` | Move the canvas around when held and moving the cursor |
| `Ctrl`/`Shift` + `Click` | Add clicked node to selection |
| `Ctrl` + `C`/`Ctrl` + `V` | Copy and paste selected nodes (without maintaining connections to outputs of unselected nodes) |
| `Ctrl` + `C`/`Ctrl` + `Shift` + `V` | Copy and paste selected nodes (maintaining connections from outputs of unselected nodes to inputs of pasted nodes) |
| `Shift` + `Drag` | Move multiple selected nodes at the same time |
| `Ctrl` + `D` | Load default graph |
| `Alt` + `+` | Canvas Zoom in |
| `Alt` + `-` | Canvas Zoom out |
| `Ctrl` + `Shift` + LMB + Vertical drag | Canvas Zoom in/out |
| `P` | Pin/Unpin selected nodes |
| `Ctrl` + `G` | Group selected nodes |
| `Q` | Toggle visibility of the queue |
| `H` | Toggle visibility of history |
| `R` | Refresh graph |
| `F` | Show/Hide menu |
| `.` | Fit view to selection (Whole graph when nothing is selected) |
| Double-Click LMB | Open node quick search palette |
| `Shift` + Drag | Move multiple wires at once |
| `Ctrl` + `Alt` + LMB | Disconnect all wires from clicked slot |
`Ctrl` can also be replaced with `Cmd` instead for macOS users
# Installing
## Windows Portable
There is a portable standalone build for Windows that should work for running on Nvidia GPUs or for running on your CPU only on the [releases page](https://github.com/comfyanonymous/ComfyUI/releases).
### [Direct link to download](https://github.com/comfyanonymous/ComfyUI/releases/latest/download/ComfyUI_windows_portable_nvidia.7z)
Simply download, extract with [7-Zip](https://7-zip.org) or with the windows explorer on recent windows versions and run. For smaller models you normally only need to put the checkpoints (the huge ckpt/safetensors files) in: ComfyUI\models\checkpoints but many of the larger models have multiple files. Make sure to follow the instructions to know which subfolder to put them in ComfyUI\models\
If you have trouble extracting it, right click the file -> properties -> unblock
The portable above currently comes with python 3.13 and pytorch cuda 13.0. Update your Nvidia drivers if it doesn't start.
#### All Official Portable Downloads:
[Portable for AMD GPUs](https://github.com/comfyanonymous/ComfyUI/releases/latest/download/ComfyUI_windows_portable_amd.7z)
[Portable for Intel GPUs](https://github.com/comfyanonymous/ComfyUI/releases/latest/download/ComfyUI_windows_portable_intel.7z)
[Portable for Nvidia GPUs](https://github.com/comfyanonymous/ComfyUI/releases/latest/download/ComfyUI_windows_portable_nvidia.7z) (supports 20 series and above).
[Portable for Nvidia GPUs with pytorch cuda 12.6 and python 3.12](https://github.com/comfyanonymous/ComfyUI/releases/latest/download/ComfyUI_windows_portable_nvidia_cu126.7z) (Supports Nvidia 10 series and older GPUs).
#### How do I share models between another UI and ComfyUI?
See the [Config file](extra_model_paths.yaml.example) to set the search paths for models. In the standalone windows build you can find this file in the ComfyUI directory. Rename this file to extra_model_paths.yaml and edit it with your favorite text editor.
## [comfy-cli](https://docs.comfy.org/comfy-cli/getting-started)
You can install and start ComfyUI using comfy-cli:
```bash
pip install comfy-cli
comfy install
```
## Manual Install (Windows, Linux)
Python 3.14 works but some custom nodes may have issues. The free threaded variant works but some dependencies will enable the GIL so it's not fully supported.
Python 3.13 is very well supported. If you have trouble with some custom node dependencies on 3.13 you can try 3.12
torch 2.4 and above is supported but some features and optimizations might only work on newer versions. We generally recommend using the latest major version of pytorch with the latest cuda version unless it is less than 2 weeks old.
### Instructions:
Git clone this repo.
Put your SD checkpoints (the huge ckpt/safetensors files) in: models/checkpoints
Put your VAE in: models/vae
### AMD GPUs (Linux)
AMD users can install rocm and pytorch with pip if you don't have it already installed, this is the command to install the stable version:
```pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm7.2```
This is the command to install the nightly with ROCm 7.2 which might have some performance improvements:
```pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/rocm7.2```
### AMD GPUs (Experimental: Windows and Linux), RDNA 3, 3.5 and 4 only.
These have less hardware support than the builds above but they work on windows. You also need to install the pytorch version specific to your hardware.
RDNA 3 (RX 7000 series):
```pip install --pre torch torchvision torchaudio --index-url https://rocm.nightlies.amd.com/v2/gfx110X-all/```
RDNA 3.5 (Strix halo/Ryzen AI Max+ 365):
```pip install --pre torch torchvision torchaudio --index-url https://rocm.nightlies.amd.com/v2/gfx1151/```
RDNA 4 (RX 9000 series):
```pip install --pre torch torchvision torchaudio --index-url https://rocm.nightlies.amd.com/v2/gfx120X-all/```
### Intel GPUs (Windows and Linux)
Intel Arc GPU users can install native PyTorch with torch.xpu support using pip. More information can be found [here](https://pytorch.org/docs/main/notes/get_start_xpu.html)
1. To install PyTorch xpu, use the following command:
```pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/xpu```
This is the command to install the Pytorch xpu nightly which might have some performance improvements:
```pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/xpu```
### NVIDIA
Nvidia users should install stable pytorch using this command:
```pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu130```
This is the command to install pytorch nightly instead which might have performance improvements.
```pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/cu132```
#### Troubleshooting
If you get the "Torch not compiled with CUDA enabled" error, uninstall torch with:
```pip uninstall torch```
And install it again with the command above.
### Dependencies
Install the dependencies by opening your terminal inside the ComfyUI folder and:
```pip install -r requirements.txt```
After this you should have everything installed and can proceed to running ComfyUI.
### Others:
#### Apple Mac silicon
You can install ComfyUI in Apple Mac silicon (M1 or M2) with any recent macOS version.
1. Install pytorch nightly. For instructions, read the [Accelerated PyTorch training on Mac](https://developer.apple.com/metal/pytorch/) Apple Developer guide (make sure to install the latest pytorch nightly).
1. Follow the [ComfyUI manual installation](#manual-install-windows-linux) instructions for Windows and Linux.
1. Install the ComfyUI [dependencies](#dependencies). If you have another Stable Diffusion UI [you might be able to reuse the dependencies](#i-already-have-another-ui-for-stable-diffusion-installed-do-i-really-have-to-install-all-of-these-dependencies).
1. Launch ComfyUI by running `python main.py`
> **Note**: Remember to add your models, VAE, LoRAs etc. to the corresponding Comfy folders, as discussed in [ComfyUI manual installation](#manual-install-windows-linux).
#### Ascend NPUs
For models compatible with Ascend Extension for PyTorch (torch_npu). To get started, ensure your environment meets the prerequisites outlined on the [installation](https://ascend.github.io/docs/sources/ascend/quick_install.html) page. Here's a step-by-step guide tailored to your platform and installation method:
1. Begin by installing the recommended or newer kernel version for Linux as specified in the Installation page of torch-npu, if necessary.
2. Proceed with the installation of Ascend Basekit, which includes the driver, firmware, and CANN, following the instructions provided for your specific platform.
3. Next, install the necessary packages for torch-npu by adhering to the platform-specific instructions on the [Installation](https://ascend.github.io/docs/sources/pytorch/install.html#pytorch) page.
4. Finally, adhere to the [ComfyUI manual installation](#manual-install-windows-linux) guide for Linux. Once all components are installed, you can run ComfyUI as described earlier.
#### Cambricon MLUs
For models compatible with Cambricon Extension for PyTorch (torch_mlu). Here's a step-by-step guide tailored to your platform and installation method:
1. Install the Cambricon CNToolkit by adhering to the platform-specific instructions on the [Installation](https://www.cambricon.com/docs/sdk_1.15.0/cntoolkit_3.7.2/cntoolkit_install_3.7.2/index.html)
2. Next, install the PyTorch(torch_mlu) following the instructions on the [Installation](https://www.cambricon.com/docs/sdk_1.15.0/cambricon_pytorch_1.17.0/user_guide_1.9/index.html)
3. Launch ComfyUI by running `python main.py`
#### Iluvatar Corex
For models compatible with Iluvatar Extension for PyTorch. Here's a step-by-step guide tailored to your platform and installation method:
1. Install the Iluvatar Corex Toolkit by adhering to the platform-specific instructions on the [Installation](https://support.iluvatar.com/#/DocumentCentre?id=1&nameCenter=2&productId=520117912052801536)
2. Launch ComfyUI by running `python main.py`
## [ComfyUI-Manager](https://github.com/Comfy-Org/ComfyUI-Manager/tree/manager-v4)
**ComfyUI-Manager** is an extension that allows you to easily install, update, and manage custom nodes for ComfyUI.
### Setup
1. Install the manager dependencies:
```bash
pip install -r manager_requirements.txt
```
2. Enable the manager with the `--enable-manager` flag when running ComfyUI:
```bash
python main.py --enable-manager
```
### Command Line Options
| Flag | Description |
|------|-------------|
| `--enable-manager` | Enable ComfyUI-Manager |
| `--enable-manager-legacy-ui` | Use the legacy manager UI instead of the new UI (implies `--enable-manager`) |
| `--disable-manager-ui` | Disable the manager UI and endpoints while keeping background features like security checks and scheduled installation completion (requires `--enable-manager`) |
# Running
```python main.py```
### For AMD cards not officially supported by ROCm
Try running it with this command if you have issues:
For 6700, 6600 and maybe other RDNA2 or older: ```HSA_OVERRIDE_GFX_VERSION=10.3.0 python main.py```
For AMD 7600 and maybe other RDNA3 cards: ```HSA_OVERRIDE_GFX_VERSION=11.0.0 python main.py```
### AMD ROCm Tips
You can enable experimental memory efficient attention on recent pytorch in ComfyUI on some AMD GPUs using this command, it should already be enabled by default on RDNA3. If this improves speed for you on latest pytorch on your GPU please report it so that I can enable it by default.
```TORCH_ROCM_AOTRITON_ENABLE_EXPERIMENTAL=1 python main.py --use-pytorch-cross-attention```
You can also try setting this env variable `PYTORCH_TUNABLEOP_ENABLED=1` which might speed things up at the cost of a very slow initial run.
# Notes
Only parts of the graph that have an output with all the correct inputs will be executed.
Only parts of the graph that change from each execution to the next will be executed, if you submit the same graph twice only the first will be executed. If you change the last part of the graph only the part you changed and the part that depends on it will be executed.
Dragging a generated png on the webpage or loading one will give you the full workflow including seeds that were used to create it.
You can use () to change emphasis of a word or phrase like: (good code:1.2) or (bad code:0.8). The default emphasis for () is 1.1. To use () characters in your actual prompt escape them like \\( or \\).
You can use {day|night}, for wildcard/dynamic prompts. With this syntax "{wild|card|test}" will be randomly replaced by either "wild", "card" or "test" by the frontend every time you queue the prompt. To use {} characters in your actual prompt escape them like: \\{ or \\}.
Dynamic prompts also support C-style comments, like `// comment` or `/* comment */`.
To use a textual inversion concepts/embeddings in a text prompt put them in the models/embeddings directory and use them in the CLIPTextEncode node like this (you can omit the .pt extension):
```embedding:embedding_filename.pt```
## How to show high-quality previews?
Use ```--preview-method auto``` to enable previews.
The default installation includes a fast latent preview method that's low-resolution. To enable higher-quality previews with [TAESD](https://github.com/madebyollin/taesd), download the [taesd_decoder.pth, taesdxl_decoder.pth, taesd3_decoder.pth and taef1_decoder.pth](https://github.com/madebyollin/taesd/) and place them in the `models/vae_approx` folder. Once they're installed, restart ComfyUI and launch it with `--preview-method taesd` to enable high-quality previews.
## How to use TLS/SSL?
Generate a self-signed certificate (not appropriate for shared/production use) and key by running the command: `openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 3650 -nodes -subj "/C=XX/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=CommonNameOrHostname"`
Use `--tls-keyfile key.pem --tls-certfile cert.pem` to enable TLS/SSL, the app will now be accessible with `https://...` instead of `http://...`.
> Note: Windows users can use [alexisrolland/docker-openssl](https://github.com/alexisrolland/docker-openssl) or one of the [3rd party binary distributions](https://wiki.openssl.org/index.php/Binaries) to run the command example above.
<br/><br/>If you use a container, note that the volume mount `-v` can be a relative path so `... -v ".\:/openssl-certs" ...` would create the key & cert files in the current directory of your command prompt or powershell terminal.
## Support and dev channel
[Discord](https://comfy.org/discord): Try the #help or #feedback channels.
[Matrix space: #comfyui_space:matrix.org](https://app.element.io/#/room/%23comfyui_space%3Amatrix.org) (it's like discord but open source).
See also: [https://www.comfy.org/](https://www.comfy.org/)
> _psst — we're hiring!_ Help build ComfyUI: [comfy.org/careers](https://www.comfy.org/careers)
## Frontend Development
As of August 15, 2024, we have transitioned to a new frontend, which is now hosted in a separate repository: [ComfyUI Frontend](https://github.com/Comfy-Org/ComfyUI_frontend). The compiled JS files (from TS/Vue) are published to [pypi](https://pypi.org/project/comfyui-frontend-package) and installed as a dependency in ComfyUI.
### Reporting Issues and Requesting Features
For any bugs, issues, or feature requests related to the frontend, please use the [ComfyUI Frontend repository](https://github.com/Comfy-Org/ComfyUI_frontend). This will help us manage and address frontend-specific concerns more efficiently.
### Using the Latest Frontend
The new frontend is now the default for ComfyUI. However, please note:
1. The frontend in the main ComfyUI repository is updated fortnightly.
2. Daily releases are available in the separate frontend repository.
To use the most up-to-date frontend version:
1. For the latest daily release, launch ComfyUI with this command line argument:
```
--front-end-version Comfy-Org/ComfyUI_frontend@latest
```
2. For a specific version, replace `latest` with the desired version number:
```
--front-end-version Comfy-Org/ComfyUI_frontend@1.2.2
```
This approach allows you to easily switch between the stable fortnightly release and the cutting-edge daily updates, or even specific versions for testing purposes.
# QA
### Which GPU should I buy for this?
[See this page for some recommendations](https://github.com/comfyanonymous/ComfyUI/wiki/Which-GPU-should-I-buy-for-ComfyUI)

View File

@@ -74,10 +74,14 @@
state: directory state: directory
mode: '0755' mode: '0755'
# Mode 0770: the certbot renewal deploy-hook (running as the certbot user,
# a member of the haproxy group) must be able to create the temporary PEM
# file here. With 0750 the hook fails with "Permission denied" and HAProxy
# keeps serving a stale cert until it expires.
- name: Ensure /etc/haproxy/certs directory exists - name: Ensure /etc/haproxy/certs directory exists
ansible.builtin.file: ansible.builtin.file:
path: /etc/haproxy/certs path: /etc/haproxy/certs
owner: "{{ haproxy_user | default('haproxy') }}" owner: "{{ haproxy_user | default('haproxy') }}"
group: "{{ haproxy_group | default('haproxy') }}" group: "{{ haproxy_group | default('haproxy') }}"
state: directory state: directory
mode: '0750' mode: '0770'

View File

@@ -9,6 +9,7 @@ global
log /dev/log local0 log /dev/log local0
log /dev/log local1 notice log /dev/log local1 notice
stats timeout 30s stats timeout 30s
maxconn 4096
# Ubuntu systemd service handles user/group and daemonization # Ubuntu systemd service handles user/group and daemonization
# Default SSL material locations # Default SSL material locations
@@ -30,17 +31,25 @@ defaults
timeout connect 5s timeout connect 5s
timeout client 50s timeout client 50s
timeout server 50s timeout server 50s
# Slowloris protection: cap time to receive the full request/keep-alive idle
timeout http-request 10s
timeout http-keep-alive 10s
# Stats page with Prometheus metrics # Stats page with Prometheus metrics
listen stats listen stats
bind *:{{ haproxy_stats_port }} bind *:{{ haproxy_stats_port }}
mode http mode http
# Restrict to the Ouranos LAN + localhost (Alloy scrapes via localhost).
# Belt-and-suspenders alongside host-level firewalling.
acl from_internal src 10.10.0.0/16 127.0.0.0/8
http-request deny unless from_internal
stats enable stats enable
stats uri /metrics stats uri /metrics
stats refresh 15s stats refresh 15s
stats show-legends stats show-legends
stats show-node stats show-node
stats hide-version
# Prometheus metrics endpoint # Prometheus metrics endpoint
http-request use-service prometheus-exporter if { path /metrics } http-request use-service prometheus-exporter if { path /metrics }

View File

@@ -23,7 +23,6 @@ alloy_log_level: "warn"
rommie_port: 20361 rommie_port: 20361
rommie_host: "0.0.0.0" rommie_host: "0.0.0.0"
rommie_display: ":10" rommie_display: ":10"
rommie_allowed_hosts: "caliban.incus,rommie.ouranos.helu.ca"
rommie_model: Qwen3.6-27B-Q5_K_M rommie_model: Qwen3.6-27B-Q5_K_M
rommie_model_url: "http://nyx.helu.ca:29000" rommie_model_url: "http://nyx.helu.ca:29000"
rommie_provider: "openai" rommie_provider: "openai"

View File

@@ -163,3 +163,11 @@ mnemosyne_app_metrics_host: caliban.incus
mnemosyne_app_metrics_port: 23181 mnemosyne_app_metrics_port: 23181
mnemosyne_web_metrics_host: caliban.incus mnemosyne_web_metrics_host: caliban.incus
mnemosyne_web_metrics_port: 23191 mnemosyne_web_metrics_port: 23191
# Athena — two scrape targets (same shape as Mnemosyne):
# app: Django /metrics via nginx (django-prometheus)
# web: nginx-prometheus-exporter sidecar (nginx stub_status → Prometheus format)
athena_app_metrics_host: puck.incus
athena_app_metrics_port: 22481
athena_web_metrics_host: puck.incus
athena_web_metrics_port: 22491

View File

@@ -29,7 +29,11 @@ ROMMIE_GROUNDING_HEIGHT={{ rommie_grounding_height | default(1024) }}
# ============================================================================ # ============================================================================
ROMMIE_HOST={{ rommie_host | default('0.0.0.0') }} ROMMIE_HOST={{ rommie_host | default('0.0.0.0') }}
ROMMIE_PORT={{ rommie_port }} ROMMIE_PORT={{ rommie_port }}
ROMMIE_ALLOWED_HOSTS={{ rommie_allowed_hosts }}
# Idle MCP sessions are reaped after this many seconds (<=0 disables).
# Prevents unbounded StreamableHTTP transport accumulation from clients
# that drop their connection without sending an explicit DELETE.
ROMMIE_SESSION_IDLE_TIMEOUT={{ rommie_session_idle_timeout | default(1800) }}
# ============================================================================ # ============================================================================
# get_screenshot (parent-agent) output # get_screenshot (parent-agent) output

View File

@@ -9,8 +9,9 @@ This playbook deploys certbot with the Namecheap DNS plugin for DNS-01 validatio
| Installation | Python virtualenv in `/srv/certbot/.venv` | | Installation | Python virtualenv in `/srv/certbot/.venv` |
| DNS Plugin | `certbot-dns-namecheap` | | DNS Plugin | `certbot-dns-namecheap` |
| Validation | DNS-01 (supports wildcards) | | Validation | DNS-01 (supports wildcards) |
| Renewal | Systemd timer (twice daily) | | Renewal | Systemd timer (twice daily), runs as the `certbot` user |
| Certificate Output | `/etc/haproxy/certs/{domain}.pem` | | Certificate Output | Combined PEM at `haproxy_cert_path` (Titania: `/etc/haproxy/certs/ouranos.pem`) |
| HAProxy Reload | `systemctl reload haproxy` (native systemd, not Docker) |
| Metrics | Prometheus textfile collector | | Metrics | Prometheus textfile collector |
## Deployments ## Deployments
@@ -69,12 +70,23 @@ services:
# ... # ...
certbot_email: webmaster@helu.ca certbot_email: webmaster@helu.ca
certbot_cert_name: ouranos.helu.ca certbot_certificates:
certbot_domains: - cert_name: wildcard.ouranos.helu.ca
- "*.ouranos.helu.ca" domains: ["*.ouranos.helu.ca", "ouranos.helu.ca"]
- "ouranos.helu.ca"
# Where the renewal hook writes the combined fullchain+privkey PEM for HAProxy
haproxy_cert_path: /etc/haproxy/certs/ouranos.pem
``` ```
> The certbot lineage name is **`wildcard.ouranos.helu.ca`**, so the certbot
> config lives under `/srv/certbot/config/live/wildcard.ouranos.helu.ca/`. The
> combined PEM that HAProxy actually serves is a separate file at
> `haproxy_cert_path` (`ouranos.pem`) written by the renewal hook — do not
> confuse the two.
>
> The playbook also supports the single-cert form (`certbot_cert_name` +
> `certbot_domains`) for hosts with one certificate.
### 3. Deploy ### 3. Deploy
```bash ```bash
@@ -91,9 +103,9 @@ ansible-playbook certbot/deploy.yml --limit titania.incus
| `/srv/certbot/credentials/namecheap.ini` | Namecheap API credentials (600 perms) | | `/srv/certbot/credentials/namecheap.ini` | Namecheap API credentials (600 perms) |
| `/srv/certbot/hooks/renewal-hook.sh` | Post-renewal script | | `/srv/certbot/hooks/renewal-hook.sh` | Post-renewal script |
| `/srv/certbot/hooks/cert-metrics.sh` | Prometheus metrics script | | `/srv/certbot/hooks/cert-metrics.sh` | Prometheus metrics script |
| `/etc/haproxy/certs/ouranos.helu.ca.pem` | Combined cert for HAProxy (Titania) | | `/etc/haproxy/certs/ouranos.pem` | Combined cert for HAProxy (Titania), written by the renewal hook |
| `/etc/systemd/system/certbot-renew.service` | Renewal service unit | | `/etc/sudoers.d/certbot-haproxy-reload` | Scoped sudo rule letting certbot run `systemctl reload haproxy` |
| `/etc/systemd/system/certbot-renew.timer` | Twice-daily renewal timer | | `/etc/systemd/system/certbot-renew.service` | Renewal service unit (runs as the `certbot` user) |
| `/etc/systemd/system/certbot-renew.timer` | Twice-daily renewal timer | | `/etc/systemd/system/certbot-renew.timer` | Twice-daily renewal timer |
## Renewal Process ## Renewal Process
@@ -105,10 +117,36 @@ ansible-playbook certbot/deploy.yml --limit titania.incus
- Waits 120 seconds for propagation - Waits 120 seconds for propagation
- Validates and downloads new certificate - Validates and downloads new certificate
- Runs `renewal-hook.sh` - Runs `renewal-hook.sh`
4. Renewal hook: 4. Renewal hook (`renewal-hook.sh`, run via certbot's `--deploy-hook`):
- Combines fullchain + privkey into HAProxy format - Combines fullchain + privkey into the HAProxy PEM at `haproxy_cert_path`
- Reloads HAProxy via `docker compose kill -s HUP haproxy` - Reloads native HAProxy via `sudo -n systemctl reload haproxy`
- Updates Prometheus metrics - Always refreshes Prometheus metrics (even on failure — see below)
> **HAProxy on Titania runs natively under systemd, not in Docker.** The hook
> reloads it with `systemctl reload haproxy`. (Only Casdoor runs in Docker on
> Titania.)
### Permission model (why renewals can silently fail)
The renewal timer runs the hook as the unprivileged **`certbot`** user, so three
permissions must line up or the renewed cert never reaches HAProxy:
| Resource | Required state | Provided by |
|----------|----------------|-------------|
| `/etc/haproxy/certs` | `0770`, group `haproxy`; `certbot` is a member of `haproxy` | `haproxy/deploy.yml` (mode) + `certbot/deploy.yml` (group membership) |
| `systemctl reload haproxy` | allowed for `certbot` via sudo | `/etc/sudoers.d/certbot-haproxy-reload` |
| Prometheus textfile dir | group-writable by `certbot` | `certbot/deploy.yml` |
If any of these is wrong, the hook fails. **Certbot treats a deploy-hook failure
as a non-fatal WARNING and still reports "renewals succeeded"** — so a broken hook
will let the live cert renew while HAProxy keeps serving the *old* file until it
expires. To make this visible, the hook now:
- checks each step and exits non-zero with an explicit
`serving a STALE certificate` error (surfaced in the certbot/journal output), and
- refreshes the Prometheus cert metrics on *every* exit, so the
`SSLCertificateExpiringSoon` / `SSLCertificateExpired` alerts keep reflecting
reality even when installation fails.
## Prometheus Metrics ## Prometheus Metrics
@@ -137,14 +175,29 @@ Example alert rule:
### View Certificate Status ### View Certificate Status
```bash ```bash
# Check certificate expiry (Titania example) # Check expiry of the cert HAProxy actually serves (Titania)
openssl x509 -enddate -noout -in /etc/haproxy/certs/ouranos.helu.ca.pem sudo openssl x509 -enddate -noout -in /etc/haproxy/certs/ouranos.pem
# Confirm HAProxy is serving it on the wire
echo | openssl s_client -connect titania.incus:8443 \
-servername grafana.ouranos.helu.ca 2>/dev/null \
| openssl x509 -noout -enddate -issuer
# Check the underlying certbot lineage (may be newer than the served file
# if the deploy hook failed to install it)
sudo openssl x509 -enddate -noout \
-in /srv/certbot/config/live/wildcard.ouranos.helu.ca/fullchain.pem
# Check certbot certificates # Check certbot certificates
sudo -u certbot /srv/certbot/.venv/bin/certbot certificates \ sudo -u certbot /srv/certbot/.venv/bin/certbot certificates \
--config-dir /srv/certbot/config --config-dir /srv/certbot/config
``` ```
> If the served file is older than the certbot lineage, the deploy hook is
> failing to install renewals. Check the hook output:
> `sudo grep -i hook /srv/certbot/logs/letsencrypt.log*` — look for
> `Permission denied`, `reload failed`, or `serving a STALE certificate`.
### Manual Renewal Test ### Manual Renewal Test
```bash ```bash

View File

@@ -374,10 +374,10 @@ MinIO specifically expects certs at `~/.minio/certs/public.crt` and `~/.minio/ce
| Certbot location | On the host itself | OCI free host | | Certbot location | On the host itself | OCI free host |
| Namecheap credentials | On the host | Only on OCI host | | Namecheap credentials | On the host | Only on OCI host |
| Cert delivery | Direct to HAProxy | Via OCI Vault → Ansible | | Cert delivery | Direct to HAProxy | Via OCI Vault → Ansible |
| Renewal hook | Docker HAProxy reload | OCI Vault upload | | Renewal hook | Combine PEM + reload HAProxy | OCI Vault upload |
| Distribution | N/A (local only) | Ansible cron on controller | | Distribution | N/A (local only) | Ansible cron on controller |
| Environments served | Ouranos sandbox only | All environments | | Environments served | Ouranos sandbox only | All environments |
| Service reload | `docker compose kill -s HUP` | `systemctl reload` per host_vars | | Service reload | `systemctl reload haproxy` (native, via scoped sudo) | `systemctl reload` per host_vars |
Titania can remain self-contained (it's working) or migrate to this centralized model later. Titania can remain self-contained (it's working) or migrate to this centralized model later.

View File

@@ -484,17 +484,35 @@ vault_casdoor_prometheus_access_key: "your-casdoor-access-key"
vault_casdoor_prometheus_access_secret: "your-casdoor-access-secret" vault_casdoor_prometheus_access_secret: "your-casdoor-access-secret"
``` ```
#### Certificate fetch fails #### TLS cert expired / not renewing on `*.ouranos.helu.ca`
**Cause**: Titania not running or certbot hasn't provisioned the cert yet. TLS for all PPLG subdomains is terminated by **Titania's native HAProxy** using
the Let's Encrypt wildcard cert managed by certbot on Titania (see
[certbot DNS-01 with Namecheap](cerbot.md)). PPLG itself holds no cert.
**Fix**: Ensure Titania is up and certbot has run: **Most likely cause**: certbot renewed the lineage but the deploy hook failed to
install the new cert into HAProxy's served PEM (`/etc/haproxy/certs/ouranos.pem`),
so HAProxy keeps serving the old file until it expires. Certbot reports such hook
failures only as a WARNING, so the renewal looks successful.
**Diagnose** (on Titania):
```bash ```bash
ansible-playbook sandbox_up.yml # Does the served file match the certbot lineage?
ansible-playbook certbot/deploy.yml sudo openssl x509 -enddate -noout -in /etc/haproxy/certs/ouranos.pem
sudo openssl x509 -enddate -noout \
-in /srv/certbot/config/live/wildcard.ouranos.helu.ca/fullchain.pem
# Look for a failing hook
sudo grep -iE 'hook|Permission denied|reload failed|STALE' /srv/certbot/logs/letsencrypt.log*
``` ```
The playbook falls back to a self-signed certificate if Titania is unavailable. **Fix**: re-run the playbooks (in this order) and force a renewal to reinstall:
```bash
ansible-playbook haproxy/deploy.yml --limit titania.incus
ansible-playbook certbot/deploy.yml --limit titania.incus
```
See the certbot doc's [permission model](cerbot.md#permission-model-why-renewals-can-silently-fail)
for the `certbot`-user permissions the hook depends on.
#### OAuth2 redirect loops #### OAuth2 redirect loops

View File

@@ -4,6 +4,7 @@ terraform {
required_providers { required_providers {
incus = { incus = {
source = "lxc/incus" source = "lxc/incus"
version = "~> 1.0"
} }
} }
} }