UI Logs vs Artifacts: Which is Best for Debugging a Failed GitHub Actions Deployment?
0 reputation · 14 May 2020, 22:58 UTC
0 reputation · 14 May 2020, 22:58 UTC
Determine whether a deployment that fails a step automatically marks the required status check as failed, and evaluate the trade‑off between using the Actions UI logs and creating an artifact for debugging.
GitHub Actions retains job logs for 90 days (public or private repositories), but the UI offers no cross‑job search and limits visibility to the current workflow run. Artifacts can store larger, searchable log fragments, yet they consume storage and may incur costs on paid plans. The interaction between a deployment’s "error" status and the required status check gate remains undocumented.
34775 reputation · 15 May 2020, 08:04 UTC
Under GitHub Actions' default behavior, a deployment step that exits non-zero fails the job, and the job's check run concludes as failure. A required status check that requires that check run then fails too, and the Deployments page shows the same run as "error." You normally do not need an extra API call to force the gate to fail.
For debugging, use both: UI logs for immediate triage, artifacts for anything you need to search, compare across runs, or keep longer.
Well-established behavior: a step that exits non-zero fails the job; a failed job's check run reports a failure conclusion; branch protection that requires a check blocks the merge or deploy while that check is not successful.
Setup-dependent behavior: whether your deployment is represented by a standard deployment check run, whether the workflow actually reports status for that deployment, and whether enterprise policies or custom check-run logic change the conclusion. If the deployment is triggered by a separate workflow or third-party integration that never posts status, the required check can stay pending instead of failing. Treat "the check fails automatically" as the expected default and verify it in your repository rather than assuming it.
| Aspect | UI logs | Artifacts |
|---|---|---|
| Retention | Job logs kept for a limited window (90 days by default) | Configurable retention; plan-dependent |
| Search | Current run only; no cross-job or cross-run search | Downloadable and scriptable; fetchable via API |
| Size | Very large step output can be truncated in the browser | Suited to larger files, within plan limits |
| Cost | Included | Counts against storage quota; may cost on paid plans |
| Access | Immediate in the browser | Requires a download or API call |
| Best for | Quick triage of the failing step | Post-mortems, cross-run comparison, sharing |
Neither tool is strictly "best." UI logs win on speed; artifacts win on persistence and searchability. The practical answer is to keep the failing command's output as an artifact while you triage in the UI.
deploy.log.- name: Upload deploy logs on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: deploy-logs-${{ github.run_id }}
path: deploy.log
retention-days: 90
Adjust the action version and retention to match your repository's current configuration.
gh api repos/{owner}/{repo}/commits/{ref}/check-runs \
--jq '.check_runs[] | {name, status, conclusion}'
Is your deployment reported by a standard deployment check run in the same workflow, or by a separate workflow or third-party integration that may not post status? If it is the latter, the required check may remain pending, and you would need to post an explicit failing check run via the Checks API or make the deployment job itself the required check. If it is the former, the default behavior above applies.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 15 May 2020, 08:27 UTC
Two points worth adding to the comparison already given.
The UI-versus-artifact framing skips gh run view --log-failed, which pulls only the failed steps' logs from a run without uploading anything. For cross-run diagnosis you can script gh run list plus gh run view --log and grep locally — that covers most of the "no cross-job search" gap without spending artifact storage. It still inherits the 90-day log retention window, so artifacts remain the answer only when you need diagnostics to outlive that or to be structured (JUnit XML, bundled logs).
On the status-check question: the common reason a deployment shows "error" while the required check never goes red is that the failing step was neutralized — continue-on-error: true, or a deploy script that catches the error and exits 0. The job then concludes success even though the deployment status object says error, because those are independent: the gate reads the check-run conclusion, not the deployment status. A quick verification in a throwaway repo: run the deploy step with exit 1 once plainly and once with continue-on-error, and watch the required check on a test PR each time.
One caution on the if: failure() artifact pattern: failing deploy steps are exactly where tokens and environment dumps end up in verbose output, so scrub before upload. Verify current retention and storage pricing for your plan before relying on the 90-day figure.