How We Built and Deployed PageSpeed by DeployHQ: A Modern Docker-Based Architecture

Devops & Infrastructure, Docker, Launches, and Tips & Tricks

How We Built and Deployed PageSpeed by DeployHQ: A Modern Docker-Based Architecture

At DeployHQ, we practice what we preach. When we decided to build PageSpeed by DeployHQ—a free web performance analysis tool—we wanted to showcase a modern, production-ready deployment workflow that demonstrates the power of automated deployments.

In this post, we'll walk you through the technical architecture and deployment strategy we used to get PageSpeed from development to production. For more details on what PageSpeed can do for your websites, check out our introduction to PageSpeed by DeployHQ.

The Challenge

We needed to build a web performance analyzer that:

  • Analyzes websites using Google PageSpeed Insights
  • Provides AI-powered recommendations
  • Handles SSL certificates automatically
  • Deploys with zero downtime
  • Scales efficiently on a simple Ubuntu VPS

The result is a clean, containerized application that demonstrates best practices in modern deployment workflows.

Architecture Overview

PageSpeed runs on a straightforward three-tier architecture:

Internet → Traefik (HTTPS/443) → Next.js App (:4000) → PostgreSQL (:5432)
                 ↓
          Let's Encrypt SSL

The Stack

Frontend & Backend: Next.js 15 with Server-Side Rendering and React 19

  • Built with the App Router for modern React patterns
  • Server-side rendering for SEO-optimized analysis results
  • Turbopack for blazing-fast development builds

Reverse Proxy: Traefik v3

  • Automatic Let's Encrypt SSL certificate management
  • Zero-configuration HTTPS with HTTP-01 challenge validation
  • Automatic certificate renewal every 60 days

Database: PostgreSQL 15

  • Persistent storage for analysis sessions
  • Internal Docker network (never exposed to internet)
  • Daily automated backups via cron

Container Orchestration: Docker Compose

  • Simple, declarative infrastructure as code
  • Production and development parity
  • Easy rollbacks and version management

Why This Stack?

Traefik for SSL Management

One of our favorite aspects of this setup is Traefik's automatic SSL certificate handling. When you first access the domain, Traefik:

  1. Requests a certificate from Let's Encrypt
  2. Validates domain ownership via HTTP-01 challenge (port 80 must be open)
  3. Stores the certificate in traefik/acme.json
  4. Automatically renews before expiration

This means zero manual SSL certificate management. No more remembering to renew certificates or worrying about expiration dates.

Docker for Consistency

Every service runs in Docker containers, which gives us:

  • Development/Production Parity: Developers work in the exact same environment as production
  • Easy Rollbacks: Each deployment is tagged with its git commit SHA
  • Resource Control: Docker resource limits prevent runaway processes
  • Simplified Dependencies: No it works on my machine problems

GitHub Container Registry for Image Distribution

Rather than building on the production server, we use GitHub Actions to:

  1. Build Docker images for linux/amd64 and linux/arm64
  2. Tag with latest and sha-<commit> for version control
  3. Push to GitHub Container Registry (ghcr.io/deployhq/pagespeed)

Production servers simply pull pre-built images, making deployments fast and consistent.

The Deployment Workflow

Here's where DeployHQ shines. Our deployment workflow is fully automated:

1. Developer Pushes Code

A developer pushes code to the main branch on GitHub.

2. Docker Image Is Built

For PageSpeed, we use GitHub Actions to build our container images. The CI/CD pipeline:

  • Builds multi-platform Docker images
  • Runs tests and linting
  • Pushes to GitHub Container Registry with two tags:
    • latest (rolling release)
    • sha-abc1234 (specific commit for rollbacks)

Alternatively, you can use DeployHQ's Docker Build feature to handle this step directly—no separate CI system required. Docker Build can:

  • Build images from your Dockerfile automatically
  • Push to Docker Hub, GitHub Container Registry, Google Container Registry, or Azure Container Registry
  • Tag with dynamic values like {revision}, {branch}, or {timestamp}

This consolidates your entire pipeline into DeployHQ, perfect for teams wanting a simpler setup.

3. DeployHQ Deploys Automatically

DeployHQ receives a webhook from GitHub and:

  • Connects to the production server via SSH
  • Runs our deployment script: ./deploy.sh --migrate
  • Pulls the latest Docker image
  • Restarts the application container with zero downtime
  • Runs database migrations if needed
  • Verifies the health check endpoint

4. Monitoring and Notifications

After deployment:

  • Health checks verify the app is responding correctly
  • DeployHQ sends success/failure notifications
  • Deployment history is tracked for easy rollbacks

Zero-Downtime Deployments

Our deploy.sh script ensures zero-downtime deployments:

#!/bin/bash
# 1. Pull new Docker image (app still running)
docker compose -f docker-compose.prod.yml pull app

# 2. Restart only the app container (database stays up)
docker compose -f docker-compose.prod.yml up -d app

# 3. Run migrations if --migrate flag provided
if [[ "$1" == "--migrate" ]]; then
  docker compose -f docker-compose.prod.yml exec app yarn migrate
fi

# 4. Verify health check passes
curl -f https://pagespeed.deployhq.com/api/health || exit 1

The key is that PostgreSQL never stops running during deployments. Only the application container is restarted, and health checks ensure the new version is working before the old container is removed.

Server Requirements

You don't need much to run PageSpeed:

  • VPS: Ubuntu 20.04+ (we use a basic Ubuntu VPS)
  • Resources: 2GB RAM, 2 CPU cores
  • Storage: 20GB disk space
  • Network: Public IP with ports 80 and 443 accessible

Security Best Practices

We follow several security principles:

Environment Variables

  • API keys stored in .env file (never committed to Git)
  • File permissions locked down: chmod 600 .env
  • Production secrets managed via secure environment variables

Firewall Configuration

sudo ufw allow 22/tcp   # SSH
sudo ufw allow 80/tcp   # HTTP (for Let's Encrypt)
sudo ufw allow 443/tcp  # HTTPS
sudo ufw enable

Database Isolation

  • PostgreSQL runs on internal Docker network only
  • Never exposed to the public internet
  • Strong passwords (32+ characters generated via openssl rand -base64 32)

Regular Updates

  • Monthly Docker image updates
  • System package updates via apt upgrade
  • Automated vulnerability scanning in CI/CD

Rollback Strategy

If something goes wrong, rollbacks are simple:

Via DeployHQ Dashboard

  1. Go to Deployment History
  2. Find the last successful deployment
  3. Click Redeploy

Manual Rollback

# Update .env with previous commit SHA
IMAGE_TAG=sha-previous-commit

# Deploy previous version
./deploy.sh

Because each deployment is tagged with its commit SHA, you can deploy any previous version instantly.

Monitoring and Observability

We monitor several key metrics:

Health Checks

  • API endpoint: https://pagespeed.deployhq.com/api/health
  • Expected response: {"status":"ok"}
  • Checked every 30 seconds

Resource Monitoring

# Container resource usage
docker stats

# Application logs
docker compose -f docker-compose.prod.yml logs -f app

Database Backups

Daily automated backups via cron:

0 2 * * * docker compose exec -T postgres \
  pg_dump -U pagespeed pagespeed_prod > /backups/pagespeed_$(date +\%Y\%m\%d).sql

Lessons Learned

After deploying PageSpeed to production, here are our key takeaways:

  1. Automate Everything: Manual deployments are error-prone. DeployHQ eliminates manual steps.
  2. SSL Should Be Automatic: Traefik's Let's Encrypt integration is a game-changer. No more certificate headaches.
  3. Docker for Parity: Development and production environments should be identical. Docker makes this trivial.
  4. Health Checks Are Critical: Always verify deployments with automated health checks before considering them successful.
  5. Tag Everything: Commit-based Docker tags make rollbacks instant and reliable.
  6. Keep Databases Running: Never restart your database during app deployments. It's unnecessary and risky.

Try It Yourself

Want to see this deployment workflow in action? Try PageSpeed by DeployHQ:

https://pagespeed.deployhq.com

Analyze your website's performance and get AI-powered optimization recommendations powered by Claude. It's completely free.

Conclusion

Building PageSpeed was an exercise in modern deployment best practices. By combining Docker, Traefik, GitHub Actions, and DeployHQ, we created a deployment pipeline that is:

  • Automated: Push to main, and it deploys automatically
  • Reliable: Zero-downtime deployments with health checks
  • Secure: Automatic SSL, isolated database, secure secrets management
  • Simple: Runs on a basic Ubuntu VPS with minimal resources

This architecture scales from side projects to production applications, and demonstrates how powerful automated deployments can be.

Want to implement a similar workflow for your projects? Sign up for DeployHQ and automate your deployments today. And if you're looking to simplify your Docker workflow, check out our new Docker Build feature to build and push images directly from DeployHQ.


Have questions about our deployment setup? Drop us a line at support@deployhq.com or join the conversation on Twitter @deployhq.

A little bit about the author

Facundo | CTO | DeployHQ | Continuous Delivery & Software Engineering Leadership - As CTO at DeployHQ, Facundo leads the software engineering team, driving innovation in continuous delivery. Outside of work, he enjoys cycling and nature, accompanied by Bono 🐶.