pgforge
v0.1.1
Published
A self-hosted platform for provisioning databases, storage, and deploying Docker Compose apps — all from a single web UI. Spin up isolated PostgreSQL databases, Redis instances, S3 buckets, and deploy apps with automatic subdomains and SSL.
Readme
PG Forge
A self-hosted platform for provisioning databases, storage, and deploying Docker Compose apps — all from a single web UI. Spin up isolated PostgreSQL databases, Redis instances, S3 buckets, and deploy apps with automatic subdomains and SSL.
Features
Core Features
- On-demand provisioning — Create isolated PostgreSQL databases with a single click
- Per-project isolation — Each database gets a dedicated user with restricted permissions
- Schema inspector — Browse table schemas for any provisioned database
- Connection strings — Copy-ready connection strings for your applications
- Light/dark mode — Theme toggle with persistence
- JWT authentication — Password-protected admin access with secure session cookies
App Deployment
- Git-based deployment — Deploy Docker Compose apps from any git repository
- Private repo support — Authenticate with GitHub, GitLab, or Bitbucket personal access tokens
- Automatic subdomains — Each app gets its own subdomain (e.g.,
myapp.yourdomain.com) - Custom domains — Optionally point your own domain to any deployed app
- Auto SSL certificates — Caddy automatically obtains and renews Let's Encrypt certificates
- Linked resources — Apps can connect to PostgreSQL, Redis, and MinIO automatically via environment variables
- Edit & update — Modify app config (git URL, port, env vars, linked services, git token) and restart in one step
- Redeploy — Pull latest code and rebuild apps with one click
- View logs — Access app and deployment logs from the UI
Storage & Caching
- MinIO/S3 buckets — Create S3-compatible buckets with auto-generated credentials
- Redis databases — Provision Redis instances with dedicated database indexes
Production-Ready Infrastructure
- Docker Compose stack — Complete containerized deployment
- PgBouncer — Connection pooling for PostgreSQL
- Caddy reverse proxy — Automatic HTTPS with Let's Encrypt
- Wildcard DNS support — Apps get automatic subdomains
Tech Stack
- Frontend: Next.js, React, TypeScript, Tailwind CSS, Radix UI / shadcn
- Backend: Next.js API routes, PostgreSQL (pg), JWT (jose)
- Infrastructure: Docker, PgBouncer, Redis, MinIO, Caddy
Getting Started
Prerequisites
- Docker & Docker Compose
- A domain name (for HTTPS)
- DNS access to create wildcard records
DNS Configuration (Important!)
You MUST configure a wildcard DNS record for app subdomains to work:
*.yourdomain.com A YOUR_SERVER_IPFor example, if your domain is forge.example.com:
*.forge.example.com A 123.456.789.012This allows PG Forge to create subdomains like:
myapp.forge.example.comapi.forge.example.coms3.forge.example.com(for MinIO console)
Without the wildcard DNS record, Let's Encrypt cannot issue SSL certificates for app subdomains.
Installation
- Clone the repository:
git clone https://github.com/yourusername/pg-forge.git
cd pg-forge- Create a
.envfile with your configuration:
# Required
DOMAIN=forge.example.com
ADMIN_PASSWORD=your-secure-password
JWT_SECRET=your-jwt-secret-min-32-chars-long
MASTER_PG_URL=postgresql://postgres:superuser-password@postgres:5432/postgres
# Optional (with defaults)
PUBLIC_PG_HOST=your-server-ip
PUBLIC_PG_PORT=5432
POSTGRES_SUPERUSER_PASSWORD=superuser-password
REDIS_PASSWORD=redis-password
MINIO_ROOT_PASSWORD=minio-password- Start the stack:
docker compose up -d- Access the UI at
https://forge.example.com
Development
yarn install
yarn devOpen http://localhost:3000 to access the UI.
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
| DOMAIN | Yes | - | Domain for Caddy auto-HTTPS (e.g., forge.example.com) |
| ADMIN_PASSWORD | Yes | - | Web UI login password |
| JWT_SECRET | Yes | - | Session token signing key (min 32 chars) |
| MASTER_PG_URL | Yes | - | PostgreSQL superuser connection string |
| PUBLIC_PG_HOST | No | localhost | Public-facing database host |
| PUBLIC_PG_PORT | No | 5432 | Public-facing database port |
| POSTGRES_SUPERUSER_PASSWORD | No | Random | Database superuser password |
| REDIS_PASSWORD | No | Random | Redis password |
| MINIO_ROOT_PASSWORD | No | Random | MinIO root password |
App Deployment Guide
Creating an App
- Go to the Apps section in the dashboard
- Click Deploy App
- Fill in the details:
- Name: Unique app name (becomes subdomain)
- Git URL: HTTPS URL to your repository
- Git Token (optional): Personal access token for private repositories
- Custom Domain (optional): Your own domain pointed at this server (e.g.,
app.yourdomain.com) - Port: The port your app listens on (default: 3000)
- Compose Service: The service name in docker-compose.yml (default: app)
- Select linked resources (optional):
- PostgreSQL database
- Redis instance
- MinIO bucket
- Add extra environment variables (optional)
- Click Deploy
Private Repositories
To deploy from a private git repository, provide a personal access token (PAT) during app creation or editing. Supported providers:
- GitHub: Generate a fine-grained PAT with
Contents: Readpermission - GitLab: Generate a project access token with
read_repositoryscope - Bitbucket: Generate an app password with
Repositories: Readpermission
The token is stored securely in the database and never returned in API responses. It is injected into the HTTPS URL at clone/pull time and stripped from .git/config immediately after.
Editing an App
- Click the pencil icon on your app in the Apps dashboard
- Update any configuration:
- Git URL, token, port, compose service, custom domain
- Linked resources (PostgreSQL, Redis, MinIO)
- Environment variables
- Click Save & restart
- PG Forge will pull latest code, regenerate the
.envfile, and rebuild the container
Auto-Injected Environment Variables
When you link resources to an app, these environment variables are automatically set:
PostgreSQL:
DATABASE_URL- Full connection stringPOSTGRES_URL- Alias for DATABASE_URL
Redis:
REDIS_URL- Redis connection string
MinIO:
S3_ENDPOINT- S3 endpoint URLS3_ACCESS_KEY- Access keyS3_SECRET_KEY- Secret keyS3_BUCKET- Bucket name
Redeploying an App
When you push new code to your git repository:
- Find your app in the Apps dashboard
- Click the redeploy button (circular arrows icon)
- PG Forge will:
- Pull the latest code
- Rebuild the Docker image
- Restart the container
- Preserve linked resources and environment variables
Viewing Logs
To troubleshoot issues:
- Click the logs button (file icon) on your app
- View Application Logs - stdout/stderr from your app
- View Deployment Logs - Docker build and compose output
- Click Refresh to get the latest logs
Docker Compose Requirements
Your app repository should include a docker-compose.yml file. PG Forge will:
- Remove host port bindings automatically (to avoid conflicts)
- Connect your app container to the internal network
- Set up a Caddy route for your subdomain
- Handle SSL certificates automatically
Example docker-compose.yml:
services:
app:
build: .
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- PORT=3000
restart: unless-stoppedArchitecture
┌─────────────────────────────────────────────────────────────┐
│ Internet │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Caddy (Port 443) │
│ - SSL termination (Let's Encrypt) │
│ - Reverse proxy to apps │
│ - Wildcard certificate support │
└──────────────────────┬──────────────────────────────────────┘
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────┐ ┌──────────────┐
│ Next.js │ │ MinIO │ │ App 1 │
│ App │ │ (S3) │ │ (subdomain) │
│ (Port 3000) │ │ │ │ │
└──────┬───────┘ └────┬─────┘ └──────┬───────┘
│ │ │
└──────────────┼──────────────┘
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
┌────────────┐ ┌──────────┐ ┌────────────┐
│ PostgreSQL │ │ Redis │ │ PgBouncer │
│ (pg18) │ │ │ │ (Pool) │
└────────────┘ └──────────┘ └────────────┘Project Structure
app/
├── page.tsx # Login page
├── dashboard/
│ ├── page.tsx # Main dashboard
│ ├── apps/page.tsx # App deployment management
│ ├── postgres/page.tsx # PostgreSQL projects
│ ├── redis/page.tsx # Redis projects
│ └── storage/page.tsx # MinIO projects
├── api/
│ ├── auth/ # Login/logout endpoints
│ ├── app-projects/ # App CRUD + edit + redeploy + logs
│ ├── minio-projects/ # MinIO bucket management
│ ├── redis-projects/ # Redis database management
│ └── projects/ # PostgreSQL CRUD + schema
components/
├── ui/ # Reusable UI primitives
├── CreateProjectModal.tsx # New PostgreSQL project dialog
├── CreateRedisProjectModal.tsx # New Redis project dialog
├── CreateMinioProjectModal.tsx # New MinIO bucket dialog
├── DeployAppModal.tsx # App deployment dialog
├── EditAppModal.tsx # App edit/update dialog
├── AppProjectsTable.tsx # App list with edit/redeploy/logs
├── PostgresProjectsTable.tsx # PostgreSQL project list
├── RedisProjectsTable.tsx # Redis project list
├── MinioProjectsTable.tsx # MinIO project list
├── SchemaViewer.tsx # Database schema display
├── ConnectionString.tsx # Copyable connection string
├── DashboardSidebar.tsx # Sidebar navigation
└── ThemeToggle.tsx # Light/dark mode toggle
lib/
├── auth.ts # JWT + session management
├── db.ts # PostgreSQL connection pools
├── app-provision.ts # App deployment logic
├── provision.ts # Database provisioning logic
├── sanitize.ts # Input validation
└── types.ts # TypeScript interfacesTroubleshooting
DNS Issues
Problem: DNS problem: NXDOMAIN looking up A for subdomain
Solution:
- Ensure wildcard DNS record exists:
*.yourdomain.com A YOUR_SERVER_IP - Wait for DNS propagation (can take 5-60 minutes)
- Check with:
dig subdomain.yourdomain.com
App Deployment Fails
Problem: Port already allocated error
Solution:
- PG Forge automatically removes host port bindings from docker-compose.yml
- Apps communicate via Docker network, not host ports
Problem: App can't connect to database
Solution:
- Check that the
.envfile was created in the app directory - Verify
DATABASE_URLenvironment variable is set - Use container names (e.g.,
postgres,redis) as hosts, notlocalhost
Private Repository Clone Fails
Problem: Authentication failed or could not read Username
Solution:
- Verify your personal access token has the correct scopes (e.g.,
Contents: Readfor GitHub) - Ensure the token has not expired
- Edit the app and re-enter the token if it was rotated
Custom Domain Not Working
Problem: Custom domain returns an error or doesn't resolve
Solution:
- Ensure your domain's DNS A record points to this server's IP
- Caddy will automatically obtain an SSL certificate for the custom domain
- Check that ports 80 and 443 are open for the ACME challenge
- You can add or change the custom domain at any time via the edit modal
SSL Certificate Issues
Problem: Caddy can't obtain certificates
Solution:
- Verify domain DNS records point to your server
- Ensure ports 80 and 443 are open
- Check Caddy logs:
docker compose logs caddy
License
Private
