2026.07 / DEPLOY DEBUGGING
Coolify Deploy Not Updating After a GitHub Push: A Practical Debug Checklist
You push a fix to GitHub, wait a minute, refresh the site, and nothing changes. The commit is definitely on the main branch. The page is definitely still old. This is one of the most common self-hosted deployment frustrations because there are several honest places for the update to get stuck.
This checklist is for the boring but useful question: where did the new version stop? It is written for GitHub-to-Coolify deployments, static sites served by nginx, Dockerfile apps, and small VPS projects where you would rather prove the deploy path than repeatedly click “Redeploy” and hope.
Do not debug from the browser tab alone. Prove the commit, the Coolify deployment, the built container, the live route, and the cache layer separately.
1. Confirm the commit reached the branch Coolify watches
Start with source control, not production. If the Coolify application is configured for main, prove that origin/main contains the commit you expect. If you deploy from another branch, check that exact branch instead.
git fetch origin
git log --oneline -5 origin/main
git status --short --branch
This catches simple mistakes: committing locally without pushing, pushing to a feature branch, or rebasing after Coolify already built a different revision. For private repositories, also remember that Coolify needs a working GitHub App or deploy-key path. GitHub's Actions documentation makes a related point for CI/CD workflows: secrets and tokens should be stored as repository or environment secrets, not pasted into workflow logs or source files.
2. Check whether Coolify actually queued a deployment
A successful Git push and a successful Coolify deployment are different events. If auto-deploy did not fire, the application may be perfectly healthy while still running yesterday's image.
In the Coolify UI, check the application's deployment list and compare the latest deployment time with the GitHub push time. If you automate via the API, trigger or inspect a deployment using the app UUID and a token that has the right team-scoped permissions. Keep the token out of shell history and reports.
# Shape only: do not paste real tokens into public docs
curl -sS -X POST https://coolify.example.com/api/v1/deploy \
-H 'Authorization: Bearer REDACTED_TOKEN_VALUE' \
-H 'Content-Type: application/json' \
--data '{"uuid":"APP_UUID","force_rebuild":false}'
If the request returns a deployment UUID, poll or open that specific deployment. If it fails, read the deployment log before changing anything else. Build logs usually point to the real layer: missing files, a bad Dockerfile, an unavailable package, or an app health check that never passes.
3. Separate “build succeeded” from “new version is running”
A green build is encouraging, but it is not proof that the new container is the one serving traffic. For static nginx sites, put a harmless version marker somewhere you can fetch: the new post URL, a metadata timestamp, or a visible heading. Then test production with a cache-busting query string.
curl -I 'https://example.com/new-page.html?v=deploy-check'
curl -sS 'https://example.com/new-page.html?v=deploy-check' | grep 'Expected heading'
If the page returns 404, the file may not be in the Docker build context, the route may be wrong, or the container may still be old. If it returns 200 but the content is old, continue down the chain instead of editing the article again.
4. Look for Docker build-context and cache mistakes
Static sites are especially easy to misread because the home page may update while a new folder or generated file is missing. Check the Dockerfile and .dockerignore. If the container only copies selected paths, make sure your new file is included.
FROM nginx:alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY . /usr/share/nginx/html
When in doubt, build locally and inspect the container instead of assuming the local working tree matches production:
docker build -t site-deploy-check .
docker run --rm -p 8080:80 site-deploy-check
For non-static apps, the same principle applies. Confirm that the image tag, build arguments, generated assets, migrations, and health check all describe the version you think you deployed.
5. Check health checks and startup order
Coolify may build a new image but keep traffic on the previous working container if the new one fails to become healthy. That is a good safety behaviour, but it can look like “Coolify ignored my push” from the outside.
- Read the failing deployment log, not just the app status badge.
- Check whether the app listens on the expected port inside the container.
- For Compose apps, confirm databases and queues have persistent volumes and realistic readiness checks.
- Make sure
/healthor the configured health path does not redirect to a login page or a canonical domain.
Health checks should prove the app is ready, not merely that a process exists. They should also avoid requiring secrets or user sessions.
6. Eliminate browser, CDN, and asset caching
If HTML is new but CSS or JavaScript is old, you are probably looking at an asset-cache problem rather than a failed deployment. Version static assets by filename or query string, and fetch them directly.
curl -I 'https://example.com/styles.css?v=20260709'
curl -sS 'https://example.com/' | grep 'styles.css?v=20260709'
For Cloudflare-fronted sites, separate origin behaviour from edge behaviour where possible. The safest public guidance is generic: purge only when you understand what is cached, prefer versioned assets for routine changes, and avoid disabling caching globally just to fix one stale file.
7. Verify discovery files after the app is live
For blogs and documentation sites, the deploy is not finished when the new page returns 200. RSS, sitemap, and LLM-readable summary files need to point at the same version. Otherwise a human can read the page while feed readers and crawlers miss it.
curl -fsS https://example.com/feed.xml | grep 'New post title'
curl -fsS https://example.com/sitemap.xml | grep 'new-post.html'
curl -fsS https://example.com/llms.txt | grep 'New post title'
I do this after the Coolify deployment finishes, not before. Submitting a URL to discovery systems before the live site serves it is an easy way to create noisy crawl failures.
8. Keep the rollback path boring
The best time to think about rollback is before you need it. A clean Git commit, a deployment UUID, and a small set of live checks make rollback straightforward: revert the commit, trigger a new deployment, and verify the same URLs again.
Avoid hot-swapping containers by hand unless there is a real production incident and the normal GitHub-to-Coolify path is blocked. Manual swaps can be useful in an emergency, but they create drift from the deployment ledger and should be synced back to Git immediately afterwards.
The short checklist
- Prove the expected commit is on the branch Coolify watches.
- Confirm a Coolify deployment was queued after that commit.
- Read the specific deployment log if it failed or stalled.
- Build locally if the problem may be Docker context, generated files, or permissions.
- Check health checks, app ports, and startup dependencies.
- Fetch live HTML and assets with cache-busting query strings.
- Verify RSS, sitemap, and other discovery files after production changes.
- Rollback through Git and Coolify rather than inventing a second deployment path.
The habit that matters is traceability. A GitHub push is one checkpoint; a Coolify deployment UUID is another; the live HTML is another. Once you treat those as separate proofs, “my deploy did nothing” usually becomes a smaller, fixable problem.