Cloud Edition Billing Setup
After deploying Kuploy Cloud edition, configure Stripe and seed billing plans to enable subscriptions.
1. Create Stripe Products
In Stripe Dashboard (use test mode for development):
- Create a Pro product with a $20/month recurring price
- Create a Business product with a $100/month recurring price
- Copy the Price IDs (e.g.,
price_1234...)
note
The Free plan doesn't need a Stripe product - it's managed entirely in the database.
2. Update Stripe Configuration
Add the Price IDs to your deployment secrets:
# Get current secrets
kubectl get secret kuploy-secrets -n kuploy -o yaml > /tmp/secrets.yaml
# Edit to add/update:
# STRIPE_PRO_PRICE_ID: <base64-encoded-price-id>
# STRIPE_BUSINESS_PRICE_ID: <base64-encoded-price-id>
# Encode your values
echo -n "price_your_pro_id" | base64
echo -n "price_your_business_id" | base64
# Apply updated secrets
kubectl apply -f /tmp/secrets.yaml
# Restart deployment to pick up changes
kubectl rollout restart deployment/kuploy -n kuploy
3. Seed Billing Plans
The database needs to be seeded with the Free, Pro, and Business plans:
# Get DATABASE_URL from secrets
export DATABASE_URL=$(kubectl get secret kuploy-secrets -n kuploy -o jsonpath='{.data.DATABASE_URL}' | base64 -d)
# Option A: Run seed from kuploy-cloud repo (if available locally)
cd kuploy-cloud/packages/cloud-server
pnpm db:seed
# Option B: Run seed via kubectl exec
kubectl exec -it deployment/kuploy -n kuploy -- npm run db:seed
# Option C: Insert plans manually via SQL
kubectl exec -it postgresql-0 -n database -- psql $DATABASE_URL -c "
INSERT INTO cloud_plans (\"planId\", name, \"stripePriceId\", \"maxProjects\", \"maxApps\", \"maxDatabases\", \"maxTeamMembers\", \"maxDomains\", \"buildMinutesPerMonth\", \"cpuLimit\", \"memoryLimit\", \"storageLimit\", price, \"isActive\", \"sortOrder\")
VALUES
('free', 'Free', NULL, 1, 2, 1, 1, 1, 100, '500m', '1Gi', '5Gi', 0, true, 0),
('pro', 'Pro', 'price_YOUR_PRO_PRICE_ID', 5, 10, 5, 5, 10, 500, '2000m', '4Gi', '20Gi', 2000, true, 1),
('business', 'Business', 'price_YOUR_BUSINESS_PRICE_ID', 20, 50, 25, 20, 50, 2000, '8000m', '16Gi', '100Gi', 10000, true, 2)
ON CONFLICT (\"planId\") DO UPDATE SET
\"stripePriceId\" = EXCLUDED.\"stripePriceId\",
\"isActive\" = EXCLUDED.\"isActive\";
"
4. Verify Plans
Check that plans are visible:
# Query the database
kubectl exec -it postgresql-0 -n database -- psql $DATABASE_URL -c "SELECT \"planId\", name, \"stripePriceId\", price FROM cloud_plans;"
# Or visit the billing page in your browser
# https://your-domain.com/billing
You should see Free, Pro, and Business plans displayed on the billing page.
5. Configure Stripe Webhooks
For production, configure webhooks in Stripe Dashboard:
- Go to Developers → Webhooks
- Add endpoint:
https://your-domain.com/api/webhooks/stripe - Select events:
checkout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.paidinvoice.payment_failed
- Copy the webhook signing secret
- Update
STRIPE_WEBHOOK_SECRETin your secrets:
# Encode the new webhook secret
echo -n "whsec_your_webhook_secret" | base64
# Update the secret in Kubernetes
kubectl edit secret kuploy-secrets -n kuploy
# Or patch it:
kubectl patch secret kuploy-secrets -n kuploy -p '{"data":{"STRIPE_WEBHOOK_SECRET":"<base64-encoded-value>"}}'
# Restart to pick up changes
kubectl rollout restart deployment/kuploy -n kuploy
Updating Price IDs Later
If you need to update Stripe price IDs after initial setup:
kubectl exec -it postgresql-0 -n database -- psql $DATABASE_URL -c "
UPDATE cloud_plans SET \"stripePriceId\" = 'price_NEW_PRO_ID' WHERE \"planId\" = 'pro';
UPDATE cloud_plans SET \"stripePriceId\" = 'price_NEW_BUSINESS_ID' WHERE \"planId\" = 'business';
"
Troubleshooting
Plans not showing on billing page
-
Verify plans exist in database:
kubectl exec -it postgresql-0 -n database -- psql $DATABASE_URL -c "SELECT * FROM cloud_plans;" -
Check
isActiveistruefor all plans -
Check browser console for API errors
Stripe checkout fails
- Verify
STRIPE_SECRET_KEYis correct - Verify
stripePriceIdin database matches Stripe Dashboard - Check Kuploy logs:
kubectl logs -n kuploy -l app.kubernetes.io/name=kuploy
Webhooks not received
- Verify webhook endpoint is accessible from internet
- Check
STRIPE_WEBHOOK_SECRETmatches Stripe Dashboard - View webhook logs in Stripe Dashboard → Developers → Webhooks → Select endpoint