Free tools Windows power users keep installed
One-click scans. No signup required.
In the FoxyInvoice deployment setup described by Lith SEO, a push to main can reach production without a manual release step—but a green workflow alone does not prove the intended code is live. The pipeline runs tests, repository and secret checks, serializes deployment, then checks container health and the served application. Its most important lesson comes from a failure: a workflow once reported success while stale containers kept running.
What happens after a push to main?
For FoxyInvoice, main is production; the author says the project does not maintain a staging environment. The sequence described in Lith SEO’s Chapter 7 is:
As an Amazon Associate I earn from qualifying purchases.
- Push: a change to the production branch starts the workflow.
- Parallel gates: tests, repository-conformance checks and Gitleaks secret scanning run before deployment. Tests include unit tests and Testcontainers-backed integration tests against temporary Postgres, including tenant-isolation coverage. The conformance check blocks newly introduced violations while allowing existing baseline debt.
- Serialized deployment: after the gates pass, the workflow connects to the host over SSH. It resets the host repository to the intended revision before rebuilding, avoiding a build from a repository left mid-update by a manual operation.
- Build and container checks: the private feed token is passed to the image build as a BuildKit secret, rather than baked into an image layer or build history. The deployment waits for each API container to become healthy; EF Core migrations are applied at container startup.
- Frontend and live checks: the SPA is rebuilt on the host and its output swapped into Caddy’s directory. The workflow checks both
/healthzendpoints and runs a smoke test. - Notification: after those checks, the workflow pings IndexNow.
These are implementation details reported by the chapter, not an independent audit of the live service. The practical distinction is that deployment is not considered verified merely because a build or SSH command completed: the workflow also checks health and the application after the change.
Why deploy directly to production?
The author’s setup treats main as production and has no staging environment. Lith SEO’s argument is that a staging system that is not actively maintained can drift from production and become an obstacle rather than a useful safety net. That is a tradeoff for this solo-operated project, not a general recommendation. A team should weigh the cost of keeping staging representative against the consequences of a production defect, its ability to test safely, and the release risk of its application.
#1 Best Overall
GitHub Actions supports workflows triggered by pushes to a default branch, along with deployment controls such as concurrency and environments that can require approval or restrict branches. GitHub also documents limits on secret access and OIDC authentication for supported cloud providers. These are platform options; the FoxyInvoice account does not establish that its pipeline uses every one of them. See GitHub’s continuous deployment documentation.
Three ways a green workflow can mislead
The chapter’s incidents are specific to the author’s system, not evidence of universal failure rates. They show why job state, runner activity and the deployed application matter alongside the workflow’s final color.
1. A failed build can leave old code serving
A build step used || echo after an error, and deployment continued with up -d --no-build. When a missing token made the build fail, the old containers remained active and the workflow appeared successful for eight hours. The corrective rule is straightforward: a failed build must stop deployment. Do not turn a build error into a successful step and then start containers without rebuilding.
Recommended Free Tools
2. A concurrency lock can leave a run pending
The author describes a cancelled run that failed to release its concurrency group, leaving later runs with zero jobs while they waited. In that incident, zero jobs combined with idle runners pointed to a stuck lock rather than ordinary runner queueing. Checking both the run’s job state and runner activity helps distinguish those conditions.
Rank #3
3. Incorrect runner labels can match no runner
A quoted string was interpreted as one literal label instead of a list of labels, so none of the self-hosted runners matched. The correction was to express the labels as a YAML list. When a job does not get assigned, inspect the labels the workflow actually requests and compare them with the labels on available runners.
Runner choice affects queueing and maintenance
FoxyInvoice’s runners are self-hosted and shared with sibling repositories. The chapter says the organization chose them after GitHub-included minutes were exhausted under a $0 spending limit, and that normal queueing is monitored. This describes that organization’s arrangement, not a GitHub-wide requirement or pricing rule. Self-hosting can change the cost and control tradeoff, but it also makes runner availability, maintenance and queue behavior part of the operator’s responsibility.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.How rollback interacts with database migrations
The described application is built on the host, so rollback does not depend on retrieving an old image from a registry: the author resets the host repository to a previous SHA and redeploys. That does not mean a database can safely be rolled back with the code. Lith SEO warns that an applied migration should not be assumed to undo itself and describes fixing forward as the operating doctrine.
The chapter also recounts two production schema edits made outside the migration process; later migrations collided with those edits and caused startup crash loops. Its guidance is to avoid out-of-band schema changes. If an emergency manual change cannot be avoided, follow it with an idempotent migration and reconcile the migration history table. These are lessons from this project’s experience, not a guarantee that every migration system behaves identically.
Best Value
What makes push-to-deploy dependable?
For this pipeline, dependable deployment depends on preserving the chain from tested revision to verified production state. The useful checks are operational, not just cosmetic:
Quick Recap
- Run meaningful tests, repository checks and secret scanning before production deployment.
- Ensure any failed build stops the deployment rather than allowing old containers to continue unnoticed.
- Serialize production deployments so pushes do not interleave.
- Keep build credentials out of image layers and history.
- Check container health and the live application after deployment; workflow completion by itself does not establish that the expected version is serving.
- Make runner labels and queue state observable, and treat database migrations as a separate rollback concern from application code.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




