How to Deploy a Dockerized Web App with GitHub Actions and Nginx
DockerGitHub ActionsNginxCI/CDWeb Deployment

How to Deploy a Dockerized Web App with GitHub Actions and Nginx

PPlkdt Labs
2026-08-07
6 min read

A practical Docker deployment guide covering GitHub Actions, Nginx, HTTPS, health checks, and rollback procedures for production web apps.

This Docker deployment tutorial provides a reusable path from a GitHub repository to a production web app on a Linux server. You will build and publish a container image, release it with GitHub Actions over SSH, place Nginx in front of the container, add HTTPS, verify health, and keep a rollback option available.

Overview

The workflow assumes you have a Dockerized web application, a GitHub repository, a Linux VPS or similar host, and a domain name pointed at that server. The exact commands vary by application and operating system, but the deployment boundaries stay consistent:

  1. Application: the app listens on a known internal port and exposes a health endpoint such as /health.
  2. Image: GitHub Actions builds a versioned Docker image and pushes it to a container registry.
  3. Server: the host pulls the selected image and runs it with a stable container name or deployment method.
  4. Edge: Nginx receives public HTTP and HTTPS traffic and proxies requests to the container.
  5. Operations: health checks, logs, backups, and rollback instructions are documented before the first release.

Keep production secrets out of the repository. Store deployment credentials in GitHub Actions secrets or environment-specific secret management, and keep runtime configuration on the server or inject it through a controlled release process. For broader host preparation, use the Linux server setup checklist.

Checklist by scenario

1. Prepare the application and Docker image

  • Confirm the application binds to 0.0.0.0 inside the container, not only localhost.
  • Document the internal listening port, required environment variables, and database or storage dependencies.
  • Add a lightweight health endpoint that returns a clear success response only when the app is ready to serve traffic.
  • Use a production Dockerfile with a predictable start command and, where practical, a non-root runtime user.
  • Add a .dockerignore file so local dependencies, credentials, build output, and unnecessary files are not copied into the image.
  • Test locally with the same environment variable names and port mapping that production will use.

Tag releases with an immutable identifier such as a commit SHA. A tag such as latest is convenient, but it makes it harder to identify exactly which build is running and complicates rollback.

2. Set up the server

  • Create a dedicated deployment user instead of using the root account for routine releases.
  • Install Docker, Nginx, Git if needed, and the operating system updates appropriate to your environment.
  • Configure the firewall to allow only the ports you need, normally SSH, HTTP, and HTTPS.
  • Create a deployment directory containing the Compose file, environment file, and release notes.
  • Make sure the deployment user can run the required Docker commands without exposing unrelated administrative access.
  • Confirm that the server has enough disk space for more than one image version during a transition.

Before automating a release, manually pull and run a known image. This separates application, Docker, and network problems from GitHub Actions problems.

3. Configure GitHub Actions deployment

A typical GitHub Actions deployment has two jobs or phases: build and publish the image, then connect to the server and release it. The workflow should:

  1. Run tests and any application build checks.
  2. Authenticate to the selected container registry.
  3. Build an image tagged with the commit SHA.
  4. Push that image to the registry.
  5. Authenticate to the server using a dedicated SSH key.
  6. Pass the image tag to a server-side release script.
  7. Pull the image, start the new container, run a health check, and stop the previous version only after validation.

Useful secrets may include the registry username and token, server hostname, SSH private key, SSH port, and production configuration values. Name secrets by environment so a staging workflow cannot accidentally use production credentials. Restrict workflows to the branches or tags that are meant to deploy.

4. Add Nginx as a reverse proxy

Nginx should be the public entry point, while the application container listens on a private or locally bound port. A minimal server block resembles this pattern:

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Replace the hostname and port with your values. Test the configuration before reloading Nginx, and check the Nginx error log if requests fail. If your application uses WebSockets, confirm that the proxy configuration includes the connection headers required by that application.

5. Enable HTTPS and verify the domain

Point the domain's DNS record at the server's public address, then verify that the hostname resolves as expected before requesting a certificate. A DNS change can be cached by resolvers, so use a DNS propagation checker and compare results from more than one resolver. The DNS propagation checker guide explains a practical verification process.

Use your certificate tooling to obtain and renew the certificate, then configure Nginx to serve HTTPS and redirect plain HTTP if that matches your application requirements. Check that the application understands forwarded HTTPS headers; otherwise, it may generate incorrect URLs or create redirect loops. For that specific failure mode, see how to fix too many redirects after a proxy or SSL change.

What to double-check

  • Port ownership: Nginx should own public ports, while the container uses its internal port or a narrowly scoped local binding.
  • Health behavior: test the endpoint from the server and through the public hostname. A container being “running” does not prove the app is usable.
  • Environment values: verify database URLs, allowed origins, cookie settings, API keys, and application secrets for the target environment.
  • Database changes: run migrations as a deliberate release step. Do not assume an application restart safely performs every schema change.
  • Logs: know where to inspect container logs, Nginx logs, system logs, and GitHub Actions output.
  • DNS: check the A or AAAA record, proxy mode if applicable, and whether an old record still points to a previous server.
  • SSH security: test the deployment key with the same username, hostname, and port used by the workflow.
  • Rollback: retain the previous image tag and document the command or script that restores it.

Common mistakes

Using only a floating image tag. If every release uses latest, logs may not tell you which build is active. Use immutable tags and record the deployed tag.

Publishing the application port directly. Exposing the container port to the internet can bypass Nginx, TLS, and access controls. Bind it locally where possible.

Deploying before testing the image. Build failures, missing files, incorrect architecture, and absent environment variables are easier to diagnose before the image reaches production.

Replacing the old container too early. Pull and validate the new version first. If the new process fails immediately, a controlled rollback is safer than leaving the site unavailable.

Ignoring proxy headers. Incorrect forwarded headers can affect secure cookies, redirects, client IP handling, and URL generation.

Assuming DNS is the only cause of an unreachable site. Check DNS, firewall rules, Nginx status, certificate configuration, container status, and application logs in that order. For a wider release diagnosis, use the deployment troubleshooting checklist.

When to revisit

Revisit this deployment checklist before a major release, a seasonal traffic period, a domain or DNS change, a server migration, or any change to the CI/CD workflow. Also review it when you change the base image, Docker version, registry, SSH access, Nginx configuration, certificate process, database schema, or application health checks.

At each review, perform one complete release to a staging environment, verify the health endpoint through Nginx, inspect the logs, and practice restoring the previous image. Confirm that backups are recent and that someone other than the original author can follow the rollback instructions. If the application needs a staging hostname, the guide to creating a staging subdomain covers the domain and indexing considerations.

The practical goal is not a complicated pipeline. It is a predictable sequence with visible inputs, a tested health check, least-privilege access, and a known way back. Keep the workflow, server scripts, Nginx configuration, and operational notes together so the next deployment remains repeatable when tools or team members change.

Related Topics

#Docker#GitHub Actions#Nginx#CI/CD#Web Deployment
P

Plkdt Labs

Developer Tools Editor

Senior editor and content strategist. Writing about technology, design, and the future of digital media. Follow along for deep dives into the industry's moving parts.