Share an HTML report from Argo Workflows
Argo runs the pipeline that produces the report. It does not solve the part after that, and on Kubernetes the part after that is harder than usual.
- Artifacts live in your object store. S3, GCS or MinIO, reachable by the
workflow controller. A human who wants to read
report.htmlneeds credentials to that bucket, or a person with credentials to fetch it for them. - The Argo UI is behind the cluster. It is normally exposed on an internal
ingress, or through
kubectl port-forward, or not at all. The analyst waiting on the nightly pipeline is not going to port-forward. - HTML artifacts download rather than render. Argo serves artifacts with headers chosen so a workflow cannot script the Argo UI — the right call, and it means your report arrives as a file, not a page.
So the result of a pipeline that ran on forty pods gets screenshotted by the one person with cluster access and pasted into Slack.
Publish from an exit handler
Put a scoped token (reports:write only) in a Kubernetes
Secret, create the report once, and PATCH that id from the workflow's
onExit handler:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: nightly-eval-
spec:
entrypoint: pipeline
onExit: publish-report
arguments:
parameters:
- name: report-id
value: rep_xxxxxxxx
volumeClaimTemplates:
- metadata: { name: work }
spec:
accessModes: ["ReadWriteOnce"]
resources: { requests: { storage: 1Gi } }
templates:
- name: pipeline
container:
image: ghcr.io/acme/eval:latest
command: [bash, -c]
args: ["python -m eval --html /work/report.html"]
volumeMounts: [{ name: work, mountPath: /work }]
- name: publish-report
container:
image: ghcr.io/acme/curl-jq:latest
command: [bash, -c]
args:
- |
curl -fsS -X PATCH \
"https://commareports.com/api/v1/reports/{{workflow.parameters.report-id}}" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html /work/report.html \
--arg title "Nightly eval — {{workflow.name}} ({{workflow.status}})" \
'{title: $title, html: $html}')"
env:
- name: COMMA_API_TOKEN
valueFrom:
secretKeyRef: { name: comma-api, key: token }
volumeMounts: [{ name: work, mountPath: /work }]
Three details carry it:
onExitruns either way. A workflow that failed halfway is exactly the workflow whose report someone wants, and an exit handler is the only place in Argo that reliably runs for both outcomes.{{workflow.status}}in the title means the reader can see from the report list whether that run finished, without opening anything.- The token is a
secretKeyRef, not an env literal, so it is not sitting in the Workflow spec that anyone withget workflowscan read.
Your artifact repository stays exactly as it is. Keeping the raw outputs in
the bucket costs nothing; the published report is for the people who are not
going to kubectl anything.
What the published copy adds
- A URL outside the cluster. No VPN, no port-forward, no bucket credentials — visibility is private, team, domain-gated, or link, decided per report.
- It renders. HTML is stored verbatim and served in a sandboxed iframe with scripts enabled, so a plotly chart or a filterable table still works.
- One URL across every run. Each run appends a revision at the same address, and any two revisions can be diffed — which is what makes a nightly pipeline legible over time.
- Comments anchored to the content. The analyst highlights the metric that moved and pins a thread to it — see commenting on HTML.
- Announcements. A webhook on
revision.createdposts each run into Slack.
Limits
- HTML body: 5 MB. Larger outputs go in as assets at 25 MB per file, 250 MB per report.
- A report that fetches sibling data files at view time can't, from the sandbox — publish a static digest with the archive attached, as in sharing an Allure report.
- Rate limit: 60 requests/minute per token. A fan-out workflow that publishes from every parallel pod should publish once from the exit handler instead.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.
Related
- Publish from CI — the general pipeline pattern
- Share a Nextflow report — the other pipeline-on-a-cluster case
- Share an LLM eval report · Scheduled HTML reports