2026.06 / DEPLOY CHECKLIST
Coolify Static Site Deploy Checklist: GitHub to Nginx Without Stale CSS
A static site should be the easiest thing in the world to deploy. Commit files, push to GitHub, let Coolify build the container, and serve the output with nginx. In practice, most mistakes are not dramatic build failures. They are quieter: the homepage updates but a post keeps old styling, a new directory is ignored by the Docker build context, or the browser keeps yesterday's CSS.
This is the checklist I use for this blog when publishing plain HTML through GitHub, Docker, nginx, and Coolify. It is intentionally biased toward proof. The deploy is not done when Git says “pushed”; it is done when the live page, feed, sitemap, and assets all say the same thing.
Goal: make a static site deploy boring by verifying the source files, container, live HTML, cache-busted assets, and discovery files every time.
The simple architecture
The reliable version of the setup has very few moving parts:
local static files
↓ git push
private GitHub repository
↓ Coolify deployment
Docker image with nginx
↓ HTTPS route
production website
Coolify's application docs cover Git-based deployments, Docker build packs, and static build packs. nginx is a good final server because it is small, predictable, and excellent at serving static files. MDN's caching guidance is the reminder that “fast” and “fresh” have to be designed together: long-lived assets need versioned URLs or filenames when they change.
Start with the files, not the deploy button
Before triggering a deployment, inspect what changed locally. For a hand-built static blog, the minimum set is usually:
- the new or edited HTML page,
- the homepage card or archive link,
feed.xml,sitemap.xml,llms.txtor any other discovery page,- shared CSS and JavaScript references, if the visual shell changed.
That list matters because static sites do not have a database-backed archive that updates itself. If a post exists on disk but is missing from RSS, many readers and crawlers will never see the update.
Use one shared shell
The easiest way to make a small blog feel broken is to polish the homepage while posts use an older header, older stylesheet URL, or different footer. Every public HTML page should share the same basic shell:
<link rel="stylesheet" href="/styles.css?v=YYYYMMDD-purpose">
...
<script src="/script.js?v=YYYYMMDD-purpose"></script>
When the CSS changes, update the versioned URL everywhere. It is not enough to edit styles.css if a long cache lifetime means returning readers still have the old file. A version in the request URL gives the browser a new asset to fetch while still allowing aggressive caching for unchanged assets.
Check the Docker build context
A common static-site failure is adding a new top-level folder, committing it, and forgetting that the container build may not copy it. If the Dockerfile copies the whole repository, this is simple. If .dockerignore or a build script uses an allowlist, confirm the new directory is included.
# Good boring static-site shape
FROM nginx:alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY . /usr/share/nginx/html
The point is not that every site must use exactly this Dockerfile. The point is that the container, not the local folder, is what production serves. Build the image locally before pushing and catch missing files before Coolify does.
Validate HTML and structured data
Small syntax mistakes can break metadata without breaking the visible page. Parse the edited HTML, then parse any JSON-LD blocks. For article pages, I want at least:
- a canonical URL,
- Open Graph title, description, type, and URL,
- Twitter card title and description,
- Article JSON-LD with published and modified timestamps,
- safe keywords that describe the post without stuffing.
Then parse RSS and sitemap XML. If those files are malformed, a crawler or feed reader may drop the update even when the browser page looks fine.
Deploy, then verify production like a reader
The production check should include both command-line and browser-style verification:
- Fetch the homepage and the new post with a cache-busting query string.
- Confirm both return
200. - Confirm the live homepage links to the new post.
- Confirm the live post references the expected stylesheet version.
- Fetch the live stylesheet URL and check that the expected selectors exist.
- Fetch
/feed.xml,/sitemap.xml, and/llms.txt. - Open the rendered page when the change is visual, because grep cannot see cramped spacing or sticky-header overlap.
This is especially important with Coolify because a successful Git push and a successful deployment are separate events. The app should be running and healthy, and the live HTML should prove which version actually reached nginx.
Run a public hygiene scan
Infrastructure posts need one extra gate: do not leak operational details. Before publishing, scan the generated public files for real IP literals, API tokens, DKIM signing-key locations, mailbox passwords, credential filenames, and local-only credential paths.
Use placeholders in public examples:
<origin-host>
second-domain.example
REDACTED_TOKEN_VALUE
v=spf1 mx -all
Readers need the pattern, not your production credential material. A good public guide explains the shape of the system while deliberately withholding the parts that would help an attacker.
My deployment checklist
- Write or edit exactly the pages needed for the change.
- Update homepage, RSS, sitemap, and LLM-readable metadata.
- Keep every page on the same shared stylesheet and script versions.
- Parse HTML, JSON-LD, RSS, and sitemap files.
- Scan public output for credential material, real IP addresses, and sensitive paths.
- Build the Docker image locally.
- Commit with a descriptive message and push to the deploy branch.
- Trigger Coolify and wait for the deployment to finish.
- Verify live homepage, post, assets, feed, sitemap, and health.
- Only then submit discovery pings such as IndexNow.
That might sound heavy for static HTML, but the checks are fast and mostly automatable. They let the site stay simple without becoming sloppy.
Why this helps growth
Organic growth is not only about topic selection. It also depends on whether useful pages can be discovered, loaded, and trusted. A post that is missing from RSS, hidden from the sitemap, or styled with stale CSS is a weaker page than it needs to be.
The boring deploy checklist supports the interesting work: writing practical posts for people who are trying to ship their own static sites, self-hosted tools, and small internet projects. The site stays fast, the archive stays coherent, and each new page has a fair chance to be found.