All posts
launcharchitecturedockertechnical

Deploy Any Framework in Under 60 Seconds

PreviewDrop Team·May 20, 2026·7 min read

Every engineering team that isn't shipping a Next.js frontend has the same problem: designers, PMs, and QA want a live URL on every branch, and you don't have one. The workflow Vercel made standard in 2019 never reached Django shops, Rails teams, Expo projects, or Go monoliths.

We built PreviewDrop to fill that gap. Here's how we made "push a branch, get a live URL" fast enough that it happens before your CI tests finish — across any framework we can detect.

The pipeline most teams build by hand

Before we get to our architecture, let's look at what most teams do today. The standard DIY pipeline looks like this:

# .github/workflows/preview.yml — 200+ lines of YAML
name: Preview Deploy
on:
  push:
    branches-ignore: [main]
jobs:
  deploy-preview:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build Docker image
        run: docker build -t myapp:${{ github.sha }} .
      - name: Push to registry
        run: |
          docker tag myapp:${{ github.sha }} registry.example.com/myapp:${{ github.sha }}
          docker push registry.example.com/myapp:${{ github.sha }}
      - name: SSH deploy
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.STAGING_HOST }}
          script: |
            docker pull registry.example.com/myapp:${{ github.sha }}
            docker stop myapp-preview || true
            docker rm myapp-preview || true
            docker run -d --name myapp-preview \
              -p 3000:3000 \
              --label traefik.enable=true \
              --label traefik.http.routers.myapp.rule=Host(\`${{ github.sha }}.preview.example.com\`) \
              registry.example.com/myapp:${{ github.sha }}

This pipeline typically takes 4–8 minutes. It breaks when the registry auth token rotates. It requires every engineer to understand Docker networking. And there's no PR comment, no password protection, no auto-expiry, no live build logs.

How PreviewDrop does it

Our pipeline replaces those 200 lines of YAML with zero configuration. Here's the sequence from webhook to live URL:

  1. GitHub App receives the push event — branch push or PR open
  2. Framework detection — PreviewDrop inspects the repo and generates a tuned build plan
  3. Build cache — warm redeploys skip layers that haven't changed
  4. Dynamic routing picks up the running container — no DNS propagation delay
  5. Stability poll for 12 seconds — confirms the app is responding before marking it ready
  6. PR comment posted — URL lands in the PR thread automatically

The key insight: the router watches the container runtime directly. The moment the container starts with the right labels, the URL is routable. There's no DNS TTL to wait for, no load balancer registration delay. Container-start to live-URL is measured in single-digit seconds.

Where the seconds actually go

Here's a breakdown for a warm Next.js redeploy (the most common case):

git clone (shallow)  ........  3s
framework detection  ......  2s
docker build (cache hits)  .  18s
container start  ...........  4s
stability poll  ............  12s (fixed)
────────────────────────────────
Total: ~39 seconds

Warm redeploys are fast because the build cache reuses cached layers. The generated Dockerfile is deterministic — same package.json, same Gemfile, same requirements.txt produce the same layer hashes. After the first deploy on a branch, every subsequent push only rebuilds the application code layer.

Cold deploys (first push on a new branch, or a new stack) are slower — 90 seconds to 4 minutes depending on the framework. A Rails app with native extensions takes longer than a Go binary. We don't hide this; every engineer reading this is already hunting for the asterisk, and honesty earns the credibility you need on Hacker News.

The CLI: one command, any Dockerfile

If you prefer running deploys from the terminal instead of relying on GitHub webhooks, the PreviewDrop CLI handles everything with a single command:

npx previewdrop deploy --repo . --branch feat/new-dashboard

The CLI detects your framework, packages the source, submits it to the build queue, and streams live build logs back to your terminal over WebSocket. When the preview is ready, it prints the URL and copies it to your clipboard.

This is especially useful during local development — you can push a preview without pushing to GitHub, or trigger a redeploy when you've already pushed and just want to see the result faster.

What we sacrificed

No architecture post is honest without the trade-offs. Here's what we chose to leave out:

  • Single-instance worker. The build queue runs on one host with an in-memory queue. This caps us at roughly 14 concurrent previews on a CPX32 or 29 on a CPX41. If you're running 100+ PRs a day, this architecture isn't for you — yet. We'll move to Redis-backed queues and distributed workers when the ceiling becomes real.
  • Host-local build cache. The layer cache lives on the worker's disk. Cache misses on a second worker are cold. A shared distributed cache layer is on the roadmap.
  • No Kubernetes. Deliberately. Teams that need K8s-native previews already have Uffizzi, Okteto, or Porter. We're for the teams that don't want to manage a cluster just to see what their PR looks like.

Try it on your stack

PreviewDrop works with any framework it can detect: Expo web, Rails, Django, Laravel, FastAPI, Spring Boot, Go, Rust, Next.js, Express, and anything with a package.json, Gemfile, requirements.txt, go.mod, or Cargo.toml.

Start free with GitHub — no credit card, two concurrent previews on the free tier. Push a branch, get a live URL, share it with your team. Before your tests finish.

See also: Expo web preview in 2 minutes · Cloud bill tripled? Flat vs usage-based pricing · Docs — Quickstart

Ready to give every branch a live URL?

Free tier — 2 concurrent previews, no credit card required.

Start free