A reliable GitHub Actions deployment starts before the workflow runs. This checklist helps you deploy a web application safely by validating dependencies, tests, builds, secrets, environment variables, approvals, artifacts, rollback options, and post-deployment behavior. Use the reusable workflow pattern below as a starting point, then adapt the deployment step to your hosting platform.
Overview
GitHub Actions can automate the path from a code change to a running web application, but automation is only useful when each stage is explicit. A deployment workflow should answer five questions:
- Which changes are allowed to deploy?
- Which runtime, dependencies, and build commands does the application require?
- Which values are configuration, and which are sensitive secrets?
- What evidence shows that the release is healthy?
- How can the team stop or reverse the release if verification fails?
For many projects, a practical sequence is: trigger on a protected branch, install dependencies from a lockfile, run linting and tests, create a production build, preserve or pass the build output to the deployment step, deploy to the intended environment, and verify the result. Separating these concerns makes failed runs easier to diagnose and gives reviewers a clear place to inspect risk.
Before writing YAML, document the local commands that define a successful release. For example, a project may use npm ci, npm run lint, npm test, and npm run build. The exact commands depend on the repository. Keep the workflow consistent with the project’s documented setup; a deployment file should not quietly use a different package manager, Node version, output directory, or build mode.
Deployment also depends on application behavior after release. Review cache headers, asset handling, and stale content behavior with the HTTP caching headers guide. If the application is containerized, compare the workflow with the Dockerfile best practices guide so the image build and runtime assumptions agree.
Checklist by scenario
1. Before creating the workflow
- Identify the production branch and the event that should start a deployment.
- Confirm the supported runtime version and package manager.
- Confirm that the lockfile is committed and that dependency installation can run without interactive prompts.
- List required build-time and runtime environment variables separately.
- Decide whether the workflow deploys directly, publishes an artifact, or builds a container image.
- Define a health check, smoke test, or manual verification step for the deployed application.
- Document a rollback method that does not depend on editing production files by hand.
2. A basic build-and-deploy workflow
The following GitHub Actions workflow is intentionally provider-neutral. Replace the placeholder deployment command with the command or action required by your hosting platform. Pin action versions according to your team’s maintenance and review policy rather than copying an unexamined example.
name: Deploy web application
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: production-deployment
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up runtime
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm
- name: Install dependencies
run: npm ci
- name: Run quality checks
run: |
npm run lint
npm test
- name: Build application
run: npm run build
- name: Store build output
uses: actions/upload-artifact@v4
with:
name: web-build
path: dist
deploy:
needs: build
runs-on: ubuntu-latest
environment: production
permissions:
contents: read
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Download build output
uses: actions/download-artifact@v4
with:
name: web-build
path: dist
- name: Deploy application
run: ./scripts/deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
- name: Verify deployment
run: ./scripts/smoke-test.sh
This example assumes that the build produces a dist directory and that deployment scripts already exist in the repository. If the application needs secrets during the build, provide only the required values to that step. Do not place secrets in source files, committed workflow text, generated logs, URLs, or command arguments that may be printed.
3. Deploying with environment protection
Use a GitHub environment such as production to make the deployment target explicit. Environment configuration can hold target-specific variables and secrets, while protection rules can require an approval or limit which branches are eligible. Treat this as a control around the release, not as a substitute for tests.
- Use separate values for development, staging, and production.
- Give the deployment job the smallest permissions it needs.
- Require an approval for production when the application or team needs a human checkpoint.
- Keep staging deployments automatic when they provide useful feedback without exposing production credentials.
- Record which commit, artifact, or image was deployed.
4. Deploying a container image
For a containerized application, the build job should create an image with a traceable tag, run relevant checks, authenticate to the registry, and publish the image. The deployment job should reference that exact tag rather than rebuilding from an ambiguous branch name. This reduces the chance that the tested code differs from the code released. Check that the Dockerfile’s exposed port, entrypoint, runtime configuration, and health behavior match the hosting platform.
5. Pull requests and preview environments
A pull request workflow should normally validate code without receiving production credentials. Run installation, linting, tests, and a build where practical. If you create preview environments, isolate their data and permissions from production. Be cautious with workflows triggered by untrusted pull request code: a build can execute scripts from the repository, so avoid exposing secrets to that job unless the trust boundary is fully understood.
What to double-check
Secrets and permissions
Review every secret by purpose. A deployment token should not automatically provide unrelated repository or cloud privileges. Use the workflow’s permissions block to make access visible and narrow. Never use a JWT decoder or online utility with a live credential or production token; token contents can contain sensitive claims even when the token is being inspected for troubleshooting.
Dependency and runtime consistency
Make the CI runtime match the version used in local development and production. Prefer lockfile-based installation, and check that native dependencies, operating-system packages, and build tools are available on the selected runner. A green local build does not prove that the runner has the same environment. The local development environment guide can help identify assumptions that need to be made explicit.
Environment variables
Classify variables as build-time or runtime. Frontend variables may be embedded into publicly served JavaScript, so they should never contain private credentials. Confirm variable names, API base URLs, allowed origins, and feature flags for the target environment. If a deployment changes API routing or browser access, review the CORS troubleshooting guide before treating a browser error as a failed frontend build.
Verification and rollback
A deployment is not complete when the command exits successfully. Verify that the application responds, static assets load, authentication works where appropriate, and a key API or page can complete a normal request. Keep rollback instructions close to the workflow. Depending on the platform, rollback may mean selecting an earlier artifact, redeploying a known commit, switching an image tag, or restoring a previous release. Test the procedure before an incident requires it.
Common mistakes
- Deploying on every branch. Restrict production deployment triggers and use separate validation workflows for branches and pull requests.
- Rebuilding after tests. Build once, preserve the output, and deploy the same output that passed the release checks.
- Using floating runtime assumptions. Specify the runtime and package manager behavior instead of relying on whatever the runner currently provides.
- Printing configuration during debugging. Logs are useful, but environment dumps can expose secrets or sensitive endpoints. Print names and non-sensitive diagnostics instead.
- Giving every job broad permissions. Review permissions at the workflow and job level, especially when adding third-party actions.
- Skipping post-deployment checks. A successful upload does not confirm that routing, database connectivity, caching, or browser behavior is correct.
- Ignoring generated files and cache behavior. Wrong output directories or stale assets can make a correct release appear broken. Use the frontend debugging checklist when the deployment succeeds but the interface does not.
- Allowing overlapping production releases. Use concurrency controls or an equivalent platform feature so two deployments do not change the target unexpectedly.
When to revisit
Review the workflow before a seasonal planning cycle, a major release, a hosting migration, or a change in the application’s runtime. Revisit it whenever the repository changes package managers, build output, deployment target, authentication model, container base image, or environment variable names.
Also schedule a practical review after a failed deployment. Ask whether the failure was caught at the earliest useful stage, whether the error message identified the responsible layer, and whether rollback was clear. Check action versions, runner images, marketplace actions, cloud-provider commands, and security settings when the underlying tools change. These details can age faster than application code.
For the next release, use this short final pass:
- Open the workflow and confirm the trigger, branch, runtime, permissions, and environment.
- Run the same install, test, and build commands locally or in a staging workflow.
- Confirm that required secrets exist without exposing their values.
- Verify that the artifact or image being deployed is the one that was tested.
- Write down the health check and rollback command before pressing merge.
- After deployment, check both the user-facing application and a critical backend path.
A GitHub Actions deployment becomes dependable when it is understandable, repeatable, and reversible. Keep the workflow small enough to review, make environment boundaries visible, and update the checklist whenever your application or delivery platform changes.