A self-hosted Platform-as-a-Service (PaaS) for deploying student web apps — think mini Heroku or Render, built for cloud & DevOps portfolios.
Connect a public GitHub repository (or a bundled sample app), click Deploy, and get a live URL with build logs in the dashboard. StackPilot clones the repo, builds a Docker image, runs the container, and routes traffic through Nginx and a FastAPI gateway.
- Features
- How it works
- Quick start (local)
- Deploy StackPilot to the cloud
- Deploy your app on StackPilot
- Sample applications
- API reference
- Configuration
- Project structure
- Tech stack
- Monitoring (optional)
- Learning path
- Limitations & roadmap
- Troubleshooting
- Resume bullet
| Capability | Description |
|---|---|
| One-click deploy | Git clone → docker build → docker run from the dashboard |
| Multi-runtime | Node.js and Python apps (any stack that fits in Docker) |
| Public URLs | Each deployment gets http://your-platform/apps/<id>/ |
| Reverse proxy | Nginx + FastAPI gateway forward /apps/{id}/ to container ports |
| Auth | JWT registration/login; projects scoped per user |
| Live logs | Build and runtime output in the deployment panel |
| Cancel / stop / restart | Cancel in-progress builds; stop running apps; restart stopped or failed deployments |
| Remove deployments | Delete deployment records and clean up Docker images/containers |
| Student-tier safeguards | Dashboard warnings and blocks for repos too heavy for ~1 GiB RAM VMs |
| Demo repo | One-click suggestion to deploy GoodNotes |
| Sample apps | Built-in Node and Python hello-world for instant demos |
| Production-ready compose | PostgreSQL, API, React UI, Nginx in one docker compose |
| CI/CD | GitHub Actions for tests; optional deploy script for VM updates |
| Observability | Optional Prometheus + Grafana profile |
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐
│ Browser │────▶│ Nginx │────▶│ React Dashboard │
└─────────────┘ │ (port 80) │ └─────────────────┘
│ │ ┌─────────────────┐
└──────┬───────┴────▶│ FastAPI (API) │
│ └────────┬────────┘
│ │
│ ┌───────▼────────┐
│ │ PostgreSQL │
│ └────────────────┘
│ │
│ ┌───────▼────────┐
└─────────────▶│ Docker Engine │
│ (student apps) │
└────────────────┘
Deploy flow
- User registers and creates a Project with a
repo_url(GitHub HTTPS URL or local sample path). - User clicks Deploy → API creates a deployment (
pending→building→runningorfailed). - Background worker clones source, requires a root
Dockerfile, detectsEXPOSEport, builds the image, and starts a container on a random host port. - Gateway proxies
GET/POST … /apps/<deployment_id>/…tohttp://<host>:<port>/…(path prefix stripped). - Dashboard polls every few seconds; user opens the Live app link.
See docs/ARCHITECTURE.md for CI/CD and multi-cloud notes.
Requirements: Docker Desktop (or Docker Engine + Compose v2), 4 GB+ RAM recommended.
git clone https://github.com/shivin4/StackPilot.git
cd StackPilot
cp .env.example .env
docker compose up --build| URL | Purpose |
|---|---|
| http://localhost | Dashboard (Nginx → React) |
| http://localhost/api/docs | OpenAPI / Swagger UI |
| http://localhost/api/health | API health check |
First deploy (no GitHub needed)
- Open http://localhost
- Register → log in
- New project — name:
demo, repo:/samples/node-helloorhttps://github.com/shivin4/goodnotes.git - Deploy → when status is
running, open Live app (e.g. http://localhost/apps/1/)
Use Cancel on stuck building deployments; Restart / Remove on stopped or failed ones.
Stop with Ctrl+C or docker compose down.
Host the platform itself on a VM so others can use your dashboard.
| Provider | Guide | Best for |
|---|---|---|
| Azure for Students | docs/DEPLOY-AZURE.md | No credit card; students |
| AWS EC2 | docs/DEPLOY.md | AWS on resume; free tier |
Azure quick steps
# On Ubuntu Azure VM (user: azureuser)
git clone https://github.com/shivin4/StackPilot.git
cd StackPilot
bash scripts/install-azure.sh
# Re-login after script installs Docker, then:
cp .env.production.example .env
# Edit .env: PLATFORM_BASE_URL=http://YOUR_VM_PUBLIC_IP
# Optional free hostname: http://stackpilot.duckdns.org (see DuckDNS + cron on VM)
docker compose up -d --buildVM sizing: Azure B2ats_v2 (1 GiB RAM) works for the platform and small student apps. Avoid heavy multi-stage React builds on the same VM.
After deploy — replace YOUR_VM_IP or your DuckDNS name:
| URL | Purpose |
|---|---|
| http://YOUR_VM_IP | Dashboard |
| http://YOUR_VM_IP/api/docs | API docs |
| http://YOUR_VM_IP/apps/1/ | First deployed student app |
VM requirements: Ubuntu 22.04+, Docker socket available to the API container, ports 80 (and optionally 8000 for direct API access) open in the cloud firewall.
Anyone with access to your StackPilot instance can deploy their project if it meets these rules.
Recommended first test: https://github.com/shivin4/goodnotes.git (lightweight notes + todos, built for 1 GiB VMs).
The dashboard shows student-tier warnings by default. Large stacks (Next.js monorepos, databases, multi-service apps) are blocked or require confirming your repo is a minimal single-service project with a root Dockerfile.
| # | Requirement |
|---|---|
| 1 | Account on your StackPilot site (register / login) |
| 2 | Code in a public GitHub repo (https://github.com/user/repo.git) |
| 3 | Dockerfile at repository root (not in a subfolder) |
| 4 | EXPOSE line matching the port your app listens on |
| 5 | Process binds 0.0.0.0 (not only 127.0.0.1) |
| 6 | Prefer ports 3000, 8000, 5000, or 8080 |
| 7 | Small build — avoid React/Vite/Next multi-stage Docker builds on 1 GiB RAM |
| 8 | API paths — if the UI is served under /apps/<id>/, call /apps/<id>/api/... or use relative URLs |
Private repos are not supported yet (clone uses anonymous git clone --depth 1).
- New project — project name + GitHub URL
Example:https://github.com/shivin4/goodnotes.git - Select the project → Deploy now
- Wait for status
running - Open Live app →
http://YOUR_PLATFORM_IP/apps/<deployment-id>/
Node.js (Express / static build)
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm install --omit=dev
COPY . .
# If you build a frontend: RUN npm run build
EXPOSE 3000
ENV NODE_ENV=production
CMD ["node", "server/index.js"]Python (FastAPI)
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]StackPilot reads EXPOSE from the Dockerfile. If omitted, it defaults to 3000. It may retry 3000, 8000, 5000, 8080 if the container exits on the first port.
The API container does not inject a PORT environment variable today — use the same port in your app and in EXPOSE.
The gateway strips /apps/<deployment_id>/ before forwarding:
- Browser:
http://platform/apps/5/api/notes - Container:
http://container:port/api/notes
APIs rooted at /api/... and servers that serve a SPA from / work well.
SPA / Vite caveat: default Vite base: '/' loads assets from /assets/..., which breaks under a subpath. Use relative assets:
// vite.config.js
export default defineConfig({
base: './',
// ...
});Or serve the built frontend from the same Node process (see goodnotes).
| Repo | Stack | Port |
|---|---|---|
| goodnotes | Node + static HTML notes & todos (StackPilot-ready) | 3000 |
samples/node-hello (in this repo) |
Express JSON API | 3000 |
samples/python-hello (in this repo) |
FastAPI | 8000 |
Bundled under samples/ (mounted read-only at /samples in the API container):
| Path | Description |
|---|---|
/samples/node-hello |
Express app, GET / and /health |
/samples/python-hello |
FastAPI app on port 8000 |
Use these paths in repo URL when testing locally or on a VM without pushing to GitHub.
Base URL: /api (Nginx rewrites /api/* → FastAPI /*).
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/auth/register |
No | Create account |
POST |
/auth/login |
No | OAuth2 form → JWT |
GET |
/health |
No | API health |
GET |
/projects |
JWT | List your projects |
POST |
/projects |
JWT | { "name", "repo_url" } |
GET |
/deployments |
JWT | List deployments |
POST |
/deployments |
JWT | { "project_id" } — starts build |
GET |
/deployments/{id} |
JWT | Status + logs |
POST |
/deployments/{id}/stop |
JWT | Stop running app or cancel pending/building |
POST |
/deployments/{id}/restart |
JWT | Redeploy stopped or failed deployment |
DELETE |
/deployments/{id} |
JWT | Remove deployment and clean up Docker resources |
* |
/apps/{id}/… |
No* | Gateway to running app |
* Gateway is public; only running deployments respond. Manage deployments via JWT.
Interactive docs: http://localhost/api/docs (or your production host).
Copy .env.example (local) or .env.production.example (VM).
| Variable | Description |
|---|---|
DATABASE_URL |
PostgreSQL connection string |
SECRET_KEY |
JWT signing secret (32+ random chars in production) |
PLATFORM_BASE_URL |
Public platform URL, no trailing slash (used in live app links) |
VITE_API_URL |
Frontend API prefix (usually /api) |
DEPLOY_HOST |
Hostname API uses to reach Docker on the VM (host.docker.internal in Compose) |
POSTGRES_* |
Database credentials |
Generate a secret:
python -c "import secrets; print(secrets.token_urlsafe(32))"StackPilot/
├── backend/ # FastAPI API, deployer, gateway, auth
├── frontend/ # React dashboard (Vite)
├── nginx/ # Reverse proxy (UI, /api, /apps)
├── samples/ # node-hello, python-hello
├── scripts/ # install-azure.sh, install-ec2.sh, deploy-stackpilot.sh
├── docs/ # Deploy guides, architecture, 8-week learning plan
├── monitoring/ # Prometheus config (optional profile)
├── k8s/ # Sample Kubernetes manifests (learning)
├── docker-compose.yml
└── .github/workflows/ # CI + CD
| Layer | Technology |
|---|---|
| API | FastAPI, SQLAlchemy, PostgreSQL, JWT (python-jose, passlib/bcrypt) |
| Deploy engine | Docker SDK, git clone, host port mapping |
| Frontend | React 18, Vite |
| Edge | Nginx |
| Runtime | Docker Compose |
| CI/CD | GitHub Actions |
| Cloud targets | Azure VM, AWS EC2 (documented) |
docker compose --profile monitoring up -d| Service | URL |
|---|---|
| Prometheus | http://localhost:9090 |
| Grafana | http://localhost:3001 (default admin / admin) |
Structured 8-week cloud & DevOps curriculum with weekly docs and YouTube links:
Phases: Docker & Compose → CI/CD → production VM → Kubernetes samples → multi-cloud extras.
| Current limitation | Planned / workaround |
|---|---|
| Public GitHub repos only | Add deploy keys or PAT for private repos |
Root Dockerfile required |
Buildpack auto-detection (future) |
No PORT env injection |
Match EXPOSE in Dockerfile |
| Single VM / Docker host (~1 GiB RAM on student SKU) | Use lightweight apps; add swap on VM if builds OOM |
| SPA asset paths under subpath | base: './' in Vite, or plain HTML + relative /apps/<id>/api paths |
| No HTTPS in default compose | Terminate TLS at Nginx + Let's Encrypt |
GitHub OAuth fields in .env |
Optional Phase 2+ |
| Issue | Fix |
|---|---|
| Register returns 500 | On VM: docker compose exec api pip install bcrypt==4.0.1 then docker compose restart api |
git clone failed |
Repo must be public; URL must be https://github.com/...git |
No Dockerfile |
Add Dockerfile at repo root |
Container failed |
Check deployment logs in UI; verify EXPOSE and 0.0.0.0 bind |
| App 404 on assets | Set Vite base: './' or serve static files from Node |
App loads but API broken (undefined data) |
Frontend must call /apps/<id>/api/... or use relative API base (see GoodNotes) |
Build stuck on building |
Click Cancel in dashboard; docker system prune -f on VM if needed |
| Deploy OOM on 1 GiB VM | Use goodnotes or single-stage Dockerfile; add 2G swap |
| Azure region blocked | Use East Asia / South India / East US (see DEPLOY-AZURE.md) |
| SSH key permission denied (Windows) | Use WSL or fix key permissions; see deploy guide |
Built and deployed StackPilot, a self-hosted PaaS on Azure/AWS using Docker, FastAPI, PostgreSQL, React, Nginx, and GitHub Actions, enabling one-click containerized deployments with public URLs for student applications.
| Doc | Content |
|---|---|
| docs/ARCHITECTURE.md | System design & deploy flow |
| docs/DEPLOY-AZURE.md | Student-friendly Azure VM setup |
| docs/DEPLOY.md | AWS EC2 production setup |
| docs/00-START-HERE.md | 8-week learning roadmap |
StackPilot — ship student apps from GitHub to a live URL in minutes.