This guide walks you through the complete setup process for deploying the generic template from scratch.
The generic template is an extensible Docker-based deployment framework for different types of applications. It provides:
- Multiple application stacks - TypeScript (Vite + Express) and Python (FastAPI)
- Container-first deployment - Portable, reproducible builds with Docker
- Infrastructure as Code - Terraform for server provisioning
- Automated secrets management - 1Password integration
- Zero-downtime deployments - Kamal orchestration
This walkthrough will guide you through:
- Prerequisites and account setup
- 1Password vault configuration
- Terraform Cloud setup
- Infrastructure deployment
- Application deployment
Time estimate: 1-2 hours for first-time setup
-
1Password account with CLI installed (
op)- Individual or Teams account
- CLI tool: https://1password.com/downloads/command-line/
-
Terraform Cloud account (free tier)
- Sign up at: https://app.terraform.io/
-
Digital Ocean account with API token
- Sign up at: https://www.digitalocean.com/
- Create API token in Settings → API
-
Cloudflare account managing your DNS zone
- Sign up at: https://www.cloudflare.com/
- Add your domain to Cloudflare
-
Docker Hub account (or GitHub Container Registry)
- Sign up at: https://hub.docker.com/
- Create access token in Settings → Security
Install these tools on your development machine:
# macOS
brew install --cask 1password-cli
brew install terraform
gem install kamal
# Verify installations
op --version
terraform --version
kamal versionYou need a domain name managed by Cloudflare:
- Register a domain (NameCheap, Google Domains, etc.)
- Point nameservers to Cloudflare
- Verify domain is active in Cloudflare dashboard
This is the single source of truth for your project configuration:
# Edit the file
vim .env.projectSet these values:
# Must be globally unique (becomes your TFC org name)
PROJECT_NAME=yourname-generic
# Your Cloudflare domain
DNS_ROOT_ZONE=yourdomain.com
# Services and subdomains (format: service:subdomain)
# Empty subdomain means root domain
SERVICES="py:app,ts-web:"
# 1Password vault for cloud credentials
CLOUD_VAULT=cloud-providersImportant:
PROJECT_NAMEmust be globally unique (TFC org name =${PROJECT_NAME}-org)- For
SERVICES, format isservice:subdomainpy:app→ deploys Python API toapp.yourdomain.comts-web:→ deploys web app toyourdomain.com(root)
# Source the config
source .env.project
# Verify variables are set
echo $PROJECT_NAME
echo $DNS_ROOT_ZONE
echo $SERVICESCreate a vault for cloud provider credentials:
# Create vault
op vault create "cloud-providers"
# Or use existing vault and update .env.projectAdd credentials to the cloud-providers vault:
Terraform Cloud API Token:
op item create \
--category=login \
--title=TERRAFORM_CLOUD_API_TOKEN \
--vault=cloud-providers \
credential=<your-terraform-cloud-token>Get token from: https://app.terraform.io/app/settings/tokens
Docker Hub:
op item create \
--category=login \
--title=DOCKER_HUB_LOGIN \
--vault=cloud-providers \
username=<your-docker-username> \
credential=<your-docker-token>Digital Ocean:
op item create \
--category=login \
--title=DO_API_TOKEN \
--vault=cloud-providers \
credential=<your-do-token>Cloudflare:
op item create \
--category=login \
--title=CLOUDFLARE_DNS_API_TOKEN \
--vault=cloud-providers \
credential=<your-cloudflare-token>Create vaults for each deployment stage:
# Production vault
op vault create "<project-name>-production"
# Staging vault (optional)
op vault create "<project-name>-staging"
# Development vault (for local dev)
op vault create "<project-name>-development"Add application-specific secrets to stage vaults:
Google OAuth (for Python app):
op item create \
--category=login \
--title=GOOGLE_O_AUTH_CLIENT \
--vault=<project>-production \
username=<your-google-client-id> \
credential=<your-google-client-secret>Get credentials from: https://console.cloud.google.com/apis/credentials
Other secrets as needed:
- API keys
- Third-party service tokens
- Application-specific config
# Test reading credentials
bin/vault read DOCKER_HUB_USERNAME
bin/vault read DO_API_TOKEN
# Should print values without errorsSee ONE_PASSWORD.md for detailed vault structure.
# Create TFC org and workspaces
make tfc upThis creates:
- TFC organization:
<project-name>-org - Workspaces:
container-registry- Docker Hub reposproduction- Production infrastructurestaging- Staging infrastructure (optional)
# Check status
make tfc status
# Should show:
# ✓ Organization exists
# ✓ Workspaces createdSee TERRAFORM_CLOUD.md for details.
First, create Docker Hub repositories:
# Initialize Terraform
make iac container-registry init
# Review plan
make iac container-registry plan
# Deploy
make iac container-registry applyThis creates Docker Hub repos for each service.
# Initialize Terraform
make iac production init
# Review plan
make iac production plan
# Deploy infrastructure
make iac production applyThis creates:
- Digital Ocean droplet (server)
- SSH keys for access
- Cloudflare DNS records
- Firewall rules
Note: First apply may take 5-10 minutes.
# Get infrastructure outputs
make iac production output
# Should show:
# server_ip = "xxx.xxx.xxx.xxx"
# ssh_connect_command = "ssh ..."
# dns_records = { ... }
# Test SSH access
bin/ssh production
# Should connect to serverSee INFRASTRUCTURE.md for details.
First time only - setup Kamal infrastructure on server:
# Bootstrap server (installs Traefik, Docker networks, etc.)
make kamal ARGS="py production setup"If using the Python stack:
# Boot PostgreSQL database
make kamal ARGS="py production accessory boot postgres"
# Deploy Python app
make kamal ARGS="py production deploy"
# Verify
curl https://app.yourdomain.com/health
# Should return: {"status": "ok"}If using the TypeScript stack:
# Deploy web app
make kamal ARGS="ts-web production deploy"
# Verify
curl https://yourdomain.com
# Should return HTML# Check running containers
make kamal ARGS="py production ps"
# View logs
make kamal ARGS="py production logs"
make kamal ARGS="ts-web production logs"
# Check SSL certificates
curl -I https://yourdomain.com
# Should show: HTTP/2 200See KAMAL.md for deployment details.
Visit your deployed services:
- Web app: https://yourdomain.com
- Python API: https://app.yourdomain.com
- API health: https://app.yourdomain.com/health
# SSH to server
bin/ssh production
# Check resource usage
docker stats
# Should show all containers running with reasonable CPU/RAMFor Python app with Google OAuth:
- Visit: https://app.yourdomain.com/auth/google
- Should redirect to Google login
- After auth, redirects back to app
Someone else is using that project name. Change PROJECT_NAME in .env.project.
Wait 5-10 minutes for Cloudflare DNS propagation. Check with:
dig yourdomain.comCheck firewall allows your IP:
# Get your IP
curl ifconfig.me
# Add to firewall in Digital Ocean dashboardCheck logs:
make kamal ARGS="py production logs"Common issues:
- Missing environment variables in 1Password
- Database not booted (for Python)
- Health check endpoint not responding
Ensure:
- DNS points to server (check with
dig) - Port 80/443 open in firewall
- Valid email in Kamal config
After successful deployment:
-
Set up monitoring
- Uptime Robot for health checks
- Sentry for error tracking
- Log aggregation (if needed)
-
Configure backups
- Database backups (for Python stack)
- Config backups (1Password handles this)
-
Set up CI/CD (optional)
- GitHub Actions for automated deploys
- Run tests before deployment
-
Scale as needed
- Upgrade droplet size in Terraform
- Add more servers for load balancing
-
Customize your application
- Modify Python/TypeScript code
- Add new features
- Deploy updates with
make kamal ARGS="<service> production deploy"
For local development:
# Install dependencies
make install
# Build styles
make styles
# Run all dev servers
make devSee LOCAL.md for development guide.
- 1Password Setup
- Terraform Cloud Setup
- Infrastructure Guide
- Deployment Guide
- Development Guide
- DevOps Philosophy
For issues:
- Check troubleshooting sections in each guide
- Review Kamal logs:
make kamal ARGS="<service> production logs" - Check infrastructure:
make iac production output - Verify 1Password:
bin/vault read <KEY>
Running this stack:
- Digital Ocean Droplet (s-1vcpu-1gb): $6/month
- Cloudflare DNS: Free
- Terraform Cloud: Free (up to 500 resources)
- 1Password: Varies by plan
Total minimum: ~$6-15/month