zotero.fhirworx.io rendered a blank desktop: KDE+Zotero draw on Xvfb :20, but kasmvnc serves :21, and the kasmxproxy mirror that bridges them never started. The stock kasmvnc-entrypoint.sh blocks on `until [ -S /tmp/.X11-unix/X21 ]` (a filesystem socket path) while entrypoint.sh's concurrent `rm -rf /tmp/.X*` unlinks it — Xvnc keeps its fd + abstract socket open and serves a blank :21 forever. A startup race, hence the intermittent "doesn't open" with every process reporting healthy. - kasmxproxy-guard.sh: supervised watchdog that probes :20/:21 via xdpyinfo (not the racy socket file) and (re)launches kasmxproxy, self-healing if it dies. Emits structured `zotero_event=` logs to container stdout + a heartbeat file. - zotero-healthcheck.sh + compose healthcheck: detect the real failure mode (bridge down / :21 unreachable / KDE dead / stale heartbeat) that container/nginx/Xvnc liveness all miss. - promtail: extract zotero_event into a Loki label. - Grafana: Zotero Desktop dashboard + alert on sustained bridge failure. - otel-cli baked in; OTLP tracing dormant by default (enabling it requires attaching zotero to the observability network — documented opt-in, left off to preserve gateway-only isolation). Verified live: guard self-heals on kasmxproxy kill; healthcheck passes healthy / fails on stale heartbeat.
68 lines
3.1 KiB
Docker
68 lines
3.1 KiB
Docker
# syntax=docker/dockerfile:1
|
|
FROM ghcr.io/selkies-project/nvidia-egl-desktop:latest
|
|
|
|
USER root
|
|
|
|
# Security patches: upgrade all OS packages + remove vulnerable binaries
|
|
# we don't need in a Zotero-only container.
|
|
# ipp-usb — IPP-over-USB printer daemon (Go stdlib CVEs)
|
|
# yq — YAML processor (Go stdlib CVEs)
|
|
# firefox — base image browser; Zotero ships its own (expat CVEs)
|
|
RUN apt-get update \
|
|
&& apt-get upgrade -y \
|
|
&& apt-get purge -y --auto-remove ipp-usb firefox yq 2>/dev/null || true \
|
|
&& rm -f /usr/sbin/ipp-usb /usr/bin/yq \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Install Zotero
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
curl \
|
|
ca-certificates \
|
|
&& curl -sL https://raw.githubusercontent.com/retorquere/zotero-deb/master/install.sh | bash \
|
|
&& apt-get update && apt-get install -y --no-install-recommends \
|
|
zotero \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Patch Python packages with known CVEs from base image
|
|
RUN pip install --no-cache-dir --break-system-packages --upgrade "pillow>=12.1.1"
|
|
|
|
# Create desktop shortcut for Zotero
|
|
RUN mkdir -p /home/ubuntu/Desktop \
|
|
&& cp /usr/share/applications/zotero.desktop /home/ubuntu/Desktop/ \
|
|
&& chmod +x /home/ubuntu/Desktop/zotero.desktop \
|
|
&& chown -R ubuntu:ubuntu /home/ubuntu/Desktop
|
|
|
|
# Desktop-bridge watchdog + healthcheck deps.
|
|
# x11-utils → xdpyinfo (probe X reachability of :20/:21)
|
|
# procps → pgrep (detect kasmxproxy / plasmashell)
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
x11-utils \
|
|
procps \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# otel-cli: enables OTLP span emission from the bridge watchdog. Dormant by
|
|
# default (the guard only emits when OTEL_EXPORTER_OTLP_ENDPOINT is set and
|
|
# this container is attached to the `observability` network — see compose.yml).
|
|
# Best-effort install so a release-asset hiccup never breaks the image build.
|
|
ARG OTEL_CLI_VERSION=0.4.5
|
|
RUN arch="$(dpkg --print-architecture | sed -e 's/amd64/amd64/' -e 's/arm64/arm64/')" \
|
|
&& curl -fsSL "https://github.com/equinix-labs/otel-cli/releases/download/v${OTEL_CLI_VERSION}/otel-cli_${OTEL_CLI_VERSION}_linux_${arch}.tar.gz" \
|
|
-o /tmp/otel-cli.tgz \
|
|
&& tar -xzf /tmp/otel-cli.tgz -C /usr/local/bin otel-cli \
|
|
&& chmod +x /usr/local/bin/otel-cli \
|
|
&& rm -f /tmp/otel-cli.tgz \
|
|
|| echo 'WARN: otel-cli install failed; OTLP tracing will be unavailable'
|
|
|
|
# Desktop-bridge watchdog (fixes the kasmxproxy startup race) + healthcheck.
|
|
COPY infra/images/zotero/kasmxproxy-guard.sh /usr/local/bin/kasmxproxy-guard.sh
|
|
COPY infra/images/zotero/zotero-healthcheck.sh /usr/local/bin/zotero-healthcheck
|
|
COPY infra/images/zotero/kasmxproxy-guard.conf /etc/supervisor/conf.d/kasmxproxy-guard.conf
|
|
RUN chmod +x /usr/local/bin/kasmxproxy-guard.sh /usr/local/bin/zotero-healthcheck
|
|
|
|
# Detect a blank desktop (dead :20->:21 bridge), which container/nginx/Xvnc
|
|
# liveness all miss. Compose overrides interval/start_period as needed.
|
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=180s --retries=5 \
|
|
CMD /usr/local/bin/zotero-healthcheck
|
|
|
|
USER ubuntu
|