- Add SHARED_PROJECT_ID and SHARED_ENVIRONMENT_ID to all docker-compose files
- Use Dokploy's project-level variable syntax: ${{project.VARIABLE}}
- Deploy all user AI stacks to a single shared Dokploy project
- Update DOKPLOY_DEPLOYMENT.md with shared project configuration guide
- Add comprehensive SHARED_PROJECT_DEPLOYMENT.md architecture documentation
Benefits:
- Centralized management (all stacks in one project)
- Resource efficiency (no per-user project overhead)
- Simplified configuration (project-level shared vars)
- Better organization (500 apps in 1 project vs 500 projects)
How it works:
1. Portal reads SHARED_PROJECT_ID from environment
2. Docker-compose uses ${{project.SHARED_PROJECT_ID}} to reference project-level vars
3. Dokploy resolves these at runtime
4. Portal deploys user stacks as applications within the shared project
Fallback: If variables not set, falls back to legacy behavior (separate project per user)
12 KiB
Dokploy Deployment Guide
Overview
This project uses Gitea Actions to build Docker images and Dokploy to deploy them. Each branch (dev, staging, main) has its own:
- Docker image tag
- Docker Compose file
- Dokploy application
- Domain
Architecture
┌─────────────┐
│ Gitea │
│ (Source) │
└──────┬──────┘
│ push event
↓
┌─────────────┐
│ Gitea │
│ Actions │ Builds Docker images
│ (CI/CD) │ Tags: dev, staging, latest
└──────┬──────┘
│
↓
┌─────────────┐
│ Gitea │
│ Registry │ git.app.flexinit.nl/oussamadouhou/ai-stack-deployer
└──────┬──────┘
│ webhook (push event)
↓
┌─────────────┐
│ Dokploy │ Pulls & deploys image
│ (Deploy) │ Uses docker-compose.{env}.yml
└─────────────┘
Branch Strategy
| Branch | Image Tag | Compose File | Domain (suggested) |
|---|---|---|---|
dev |
dev |
docker-compose.dev.yml |
portal-dev.ai.flexinit.nl |
staging |
staging |
docker-compose.staging.yml |
portal-staging.ai.flexinit.nl |
main |
latest |
docker-compose.prod.yml |
portal.ai.flexinit.nl |
Gitea Actions Workflow
File: .gitea/workflows/docker-publish.yaml
Triggers: Push to dev, staging, or main branches
Builds:
dev branch → git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:dev
staging branch → git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:staging
main branch → git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:latest
Also creates SHA tags: {branch}-{short-sha}
Docker Compose Files
docker-compose.dev.yml
- Pulls:
git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:dev - Environment:
NODE_ENV=development - Container name:
ai-stack-deployer-dev
docker-compose.staging.yml
- Pulls:
git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:staging - Environment:
NODE_ENV=staging - Container name:
ai-stack-deployer-staging
docker-compose.prod.yml
- Pulls:
git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:latest - Environment:
NODE_ENV=production - Container name:
ai-stack-deployer
docker-compose.local.yml
- Builds locally (doesn't pull from registry)
- For local development only
- Includes volume mounts for hot reload
Shared Project Configuration (IMPORTANT)
What is Shared Project Deployment?
The portal deploys all user AI stacks as applications within a single shared Dokploy project, instead of creating a new project for each user. This provides:
- ✅ Better organization (all stacks in one place)
- ✅ Shared environment variables
- ✅ Centralized monitoring
- ✅ Easier management
How It Works
Dokploy Project: ai-stack-portal
├── Environment: deployments
│ ├── Application: john-dev
│ ├── Application: jane-prod
│ └── Application: alice-test
Setting Up the Shared Project
Step 1: Create the Shared Project in Dokploy
-
In Dokploy UI, create a new project:
- Name:
ai-stack-portal(or any name you prefer) - Description: "Shared project for all user AI stacks"
- Name:
-
Note the Project ID (visible in URL or API response)
- Example:
2y2Glhz5Wy0dBNf6BOR_-
- Example:
-
Get the Environment ID:
curl -s "http://10.100.0.20:3000/api/project.one?projectId=2y2Glhz5Wy0dBNf6BOR_-" \ -H "Authorization: Bearer $DOKPLOY_API_TOKEN" | jq -r '.environments[0].id'- Example:
RqE9OFMdLwkzN7pif1xN8
- Example:
Step 2: Configure Project-Level Variables
In the shared project (ai-stack-portal), add these project-level environment variables:
| Variable Name | Value | Purpose |
|---|---|---|
SHARED_PROJECT_ID |
2y2Glhz5Wy0dBNf6BOR_- |
The project where user stacks deploy |
SHARED_ENVIRONMENT_ID |
RqE9OFMdLwkzN7pif1xN8 |
The environment within that project |
Step 3: Reference Variables in Portal Applications
The portal's docker-compose files use Dokploy's variable syntax to reference these:
environment:
- SHARED_PROJECT_ID=${{project.SHARED_PROJECT_ID}}
- SHARED_ENVIRONMENT_ID=${{project.SHARED_ENVIRONMENT_ID}}
This syntax ${{project.VARIABLE}} tells Dokploy: "Get this value from the project-level environment variables"
Important Notes
- ⚠️ Both variables MUST be set in the shared project for deployment to work
- ⚠️ If not set, portal will fall back to creating separate projects per user (legacy behavior)
- ✅ You can have different shared projects for dev/staging/prod environments
- ✅ All 3 portal deployments (dev/staging/prod) should point to their respective shared projects
Setting Up Dokploy
Step 1: Create Dev Application
-
In Dokploy UI, create new application:
- Name:
ai-stack-deployer-dev - Type: Docker Compose
- Repository:
ssh://git@git.app.flexinit.nl:22222/oussamadouhou/ai-stack-deployer.git - Branch:
dev - Compose File:
docker-compose.dev.yml
- Name:
-
Configure Domain:
- Add domain:
portal-dev.ai.flexinit.nl - Enable SSL (via Traefik wildcard cert)
- Add domain:
-
Set Environment Variables:
Important: The portal application should be deployed inside the shared project (e.g.,
ai-stack-portal-dev).Then set these project-level variables in that shared project:
SHARED_PROJECT_ID=<your-shared-project-id> SHARED_ENVIRONMENT_ID=<your-shared-environment-id>And these application-level variables in the portal app:
DOKPLOY_URL=http://10.100.0.20:3000 DOKPLOY_API_TOKEN=<your-token> STACK_DOMAIN_SUFFIX=ai.flexinit.nl STACK_IMAGE=git.app.flexinit.nl/flexinit/agent-stack:latestThe docker-compose file will automatically reference the project-level variables using:
SHARED_PROJECT_ID=${{project.SHARED_PROJECT_ID}} SHARED_ENVIRONMENT_ID=${{project.SHARED_ENVIRONMENT_ID}} -
Configure Webhook:
- Event: Push
- Branch:
dev - This will auto-deploy when you push to dev branch
-
Deploy
Step 2: Create Staging Application
Repeat Step 1 with these changes:
- Name:
ai-stack-deployer-staging - Branch:
staging - Compose File:
docker-compose.staging.yml - Domain:
portal-staging.ai.flexinit.nl - Webhook Branch:
staging
Step 3: Create Production Application
Repeat Step 1 with these changes:
- Name:
ai-stack-deployer-prod - Branch:
main - Compose File:
docker-compose.prod.yml - Domain:
portal.ai.flexinit.nl - Webhook Branch:
main
Deployment Workflow
Development Cycle
# 1. Make changes on dev branch
git checkout dev
# ... make changes ...
git commit -m "feat: add new feature"
git push origin dev
# 2. Gitea Actions automatically builds dev image
# 3. Dokploy webhook triggers and deploys to portal-dev.ai.flexinit.nl
# 4. Test on dev environment
curl https://portal-dev.ai.flexinit.nl/health
# 5. When ready, merge to staging
git checkout staging
git merge dev
git push origin staging
# 6. Gitea Actions builds staging image
# 7. Dokploy deploys to portal-staging.ai.flexinit.nl
# 8. Final testing on staging, then merge to main
git checkout main
git merge staging
git push origin main
# 9. Gitea Actions builds production image (latest)
# 10. Dokploy deploys to portal.ai.flexinit.nl
Image Tags Explained
Each push creates multiple tags:
Example: Push to dev branch (commit abc1234)
Gitea Actions creates:
git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:dev ← Latest dev
git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:dev-abc1234 ← Specific commit
Example: Push to main branch (commit xyz5678)
Gitea Actions creates:
git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:latest ← Latest production
git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:main-xyz5678 ← Specific commit
Why?
- Branch tags (
dev,staging,latest) always point to latest build - SHA tags allow you to rollback to specific commits if needed
Rollback Strategy
Quick Rollback in Dokploy
If a deployment breaks, you can quickly rollback:
- In Dokploy UI, go to the application
- Edit the docker-compose file
- Change the image tag to a previous SHA:
image: git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:main-abc1234 - Redeploy
Manual Rollback via Git
# Find the last working commit
git log --oneline
# Revert to that commit
git revert HEAD # or git reset --hard <commit-sha>
# Push to trigger rebuild
git push origin main
Local Development
Using docker-compose.local.yml
# Build and run locally
docker-compose -f docker-compose.local.yml up -d
# View logs
docker-compose -f docker-compose.local.yml logs -f
# Stop
docker-compose -f docker-compose.local.yml down
Using Bun directly (without Docker)
# Install dependencies
bun install
# Run dev server (API + Vite)
bun run dev
# Run API only
bun run dev:api
# Run client only
bun run dev:client
Environment Variables
Required in Dokploy
DOKPLOY_URL=http://10.100.0.20:3000
DOKPLOY_API_TOKEN=<your-token>
Optional (with defaults)
PORT=3000
HOST=0.0.0.0
STACK_DOMAIN_SUFFIX=ai.flexinit.nl
STACK_IMAGE=git.app.flexinit.nl/flexinit/agent-stack:latest
RESERVED_NAMES=admin,api,www,root,system,test,demo,portal
Per-Environment Overrides
If dev/staging/prod need different configs, set them in Dokploy:
Dev:
STACK_DOMAIN_SUFFIX=dev-ai.flexinit.nl
Staging:
STACK_DOMAIN_SUFFIX=staging-ai.flexinit.nl
Prod:
STACK_DOMAIN_SUFFIX=ai.flexinit.nl
Troubleshooting
Build Fails in Gitea Actions
Check the workflow logs in Gitea:
https://git.app.flexinit.nl/oussamadouhou/ai-stack-deployer/actions
Common issues:
- AVX error: Fixed in Dockerfile (uses Node.js for build)
- Registry auth: Check
REGISTRY_TOKENsecret in Gitea
Deployment Fails in Dokploy
- Check Dokploy logs: Application → Logs
- Verify image exists:
docker pull git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:dev - Check environment variables: Make sure all required vars are set
Health Check Failing
# SSH into Dokploy host
ssh user@10.100.0.20
# Check container logs
docker logs ai-stack-deployer-dev
# Test health endpoint
curl http://localhost:3000/health
Webhook Not Triggering
- In Dokploy, check webhook configuration
- In Gitea, go to repo Settings → Webhooks
- Verify webhook URL and secret match
- Check recent deliveries for errors
Production Considerations
1. Image Size Optimization
The Docker image excludes dev files via .dockerignore:
- ✅
docs/- excluded - ✅
scripts/- excluded - ✅
.gitea/- excluded - ✅
*.md(except README.md) - excluded
Current image size: ~150MB
2. Security
- Container runs as non-root user (
nodejs:1001) - No secrets in source code (uses
.env) - Dokploy API accessible only on internal network
3. Monitoring
Set up alerts for:
- Container health check failures
- Memory/CPU usage spikes
- Deployment failures
4. Backup Strategy
- Database: This app has no database (stateless)
- Configuration: Environment variables stored in Dokploy (backed up)
- Code: Stored in Gitea (backed up)
Summary
| Environment | Domain | Image Tag | Auto-Deploy? |
|---|---|---|---|
| Dev | portal-dev.ai.flexinit.nl | dev |
✅ On push |
| Staging | portal-staging.ai.flexinit.nl | staging |
✅ On push |
| Production | portal.ai.flexinit.nl | latest |
✅ On push |
Next Steps:
- ✅ Push changes to
devbranch - ⏳ Create 3 Dokploy applications (dev, staging, prod)
- ⏳ Configure webhooks for each branch
- ⏳ Deploy and test each environment
Questions? Check the main README.md or CLAUDE.md for more details.