Git Push Deploy
Stackpad deploys your application automatically every time you push to your configured branch. This guide explains how the pipeline works, how to set it up, and what to do when things go wrong.
Connecting GitHub
Before you can deploy, you need to connect your GitHub account to Stackpad.
GitHub OAuth
When you click Connect GitHub (in organization settings or during project creation), Stackpad requests access to your GitHub account via OAuth. This grants Stackpad permission to:
- List your repositories (public and private you have access to)
- Read repository contents (to clone and build)
- Create webhooks (to receive push events)
Your GitHub access token is stored securely and used to clone private repositories during builds.
Webhook setup
When you create a web service from GitHub, Stackpad automatically installs a webhook on the repository. This webhook:
- Listens for push events (to trigger deploys) and delete events (to clean up preview branches)
- Uses HMAC-SHA256 signature verification to prevent spoofing
- Is visible in your repository’s GitHub settings under Webhooks
The webhook is automatically removed when you delete the service from Stackpad.
How deployment works
When you push to your deployment branch (e.g. main), here’s what happens step by step:
- GitHub sends a webhook to Stackpad’s API
- Stackpad matches the push to the correct service by repository URL and branch
- The deployment is queued (max 4 concurrent builds per build node)
- Stackpad clones your repository on a dedicated build node
- Your framework is detected from
package.jsonand config files - A Dockerfile is generated optimized for your framework
- The Docker image is built using BuildKit (10-minute timeout)
- The image is pushed to Stackpad’s private registry
- The compute node pulls the image
- A new container starts alongside the old one
- Stackpad runs a health check (90-second timeout)
- If healthy, Caddy switches traffic to the new container
- The old container stops after traffic drains
This process achieves zero-downtime deployments with automatic rollback on failure.
Build configuration
For most projects, Stackpad’s automatic detection handles everything. Here’s what you can customize:
Build command
Auto-detected from your package.json build script. Override it in service settings if needed.
Root directory
For monorepos, specify the directory of your app (e.g. apps/web). Stackpad detects pnpm workspaces and installs dependencies from the workspace root, then builds from the specified directory.
Package manager
Automatically detected from the lockfile:
| Lockfile | Package manager | Install command |
|---|---|---|
pnpm-lock.yaml | pnpm | pnpm install --frozen-lockfile |
yarn.lock | yarn | yarn install --frozen-lockfile |
package-lock.json | npm | npm ci |
Build environment
Builds run on dedicated build nodes:
| Resource | Value |
|---|---|
| CPU | 4 vCPU |
| RAM | 16 GB |
| Max concurrent builds | 4 per build node |
| Build timeout | 10 minutes |
Build-time environment variables are injected as Docker build args. This means NEXT_PUBLIC_* variables for Next.js are available during the build.
Viewing build logs
Every deployment has build logs that show the full output of the build process:
- Go to the Deployments tab (on the project or service page)
- Click any deployment
- The Build Logs panel shows the complete output
Build logs are essential for debugging failed deployments. They show:
- Dependency installation output
- Build command output (TypeScript errors, bundler warnings)
- Docker image creation
- Any errors that caused the build to fail
The log viewer auto-scrolls to follow new output and shows a connection indicator (green = streaming live).
Deployment status
| Status | Description | Duration |
|---|---|---|
| Queued | Waiting for a build slot | Depends on queue |
| Building | Cloning, installing, building | Up to 10 min |
| Deploying | Starting container, health check | Up to 90 sec |
| Ready | Live and serving traffic | — |
| Failed | Build or health check failed | — |
| Stopped | Manually stopped | — |
Manual deployments
Trigger a deployment manually from the dashboard without pushing code:
- Go to your service detail page
- Click Redeploy
This is useful for:
- Applying environment variable changes (they don’t auto-trigger deploys)
- Retrying a failed deployment
- Picking up a newly added database’s
DATABASE_URL
Common build issues
| Problem | Cause | Solution |
|---|---|---|
| Build timeout (10 min) | Large monorepo, slow installs | Optimize dependencies, check lockfile |
| TypeScript errors | Code doesn’t compile | Fix errors locally first, push again |
| Missing dependencies | Not in package.json | Add dependency and push |
NEXT_PUBLIC_* not working | Variable not set | Add to project environment variables |
| Monorepo build failure | Wrong root directory | Check root directory in service settings |
| Health check failure | App doesn’t start in 90s | See Rollbacks guide |
What’s next?
- Framework detection — which frameworks are supported
- Environment variables — configure your app
- Rollbacks — zero-downtime deploys and health check details
- Troubleshooting — common issues and how to fix them