Files
ai-stack-deployer/docs/DOKPLOY_DEPLOYMENT.md
Oussama Douhou 9a593b8b7c feat: add shared project deployment with Dokploy project-level variables
- 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)
2026-01-13 12:01:59 +01:00

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

  1. In Dokploy UI, create a new project:

    • Name: ai-stack-portal (or any name you prefer)
    • Description: "Shared project for all user AI stacks"
  2. Note the Project ID (visible in URL or API response)

    • Example: 2y2Glhz5Wy0dBNf6BOR_-
  3. 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

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

  1. 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
  2. Configure Domain:

    • Add domain: portal-dev.ai.flexinit.nl
    • Enable SSL (via Traefik wildcard cert)
  3. 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:latest
    

    The 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}}
    
  4. Configure Webhook:

    • Event: Push
    • Branch: dev
    • This will auto-deploy when you push to dev branch
  5. 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:

  1. In Dokploy UI, go to the application
  2. Edit the docker-compose file
  3. Change the image tag to a previous SHA:
    image: git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:main-abc1234
    
  4. 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_TOKEN secret in Gitea

Deployment Fails in Dokploy

  1. Check Dokploy logs: Application → Logs
  2. Verify image exists:
    docker pull git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:dev
    
  3. 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

  1. In Dokploy, check webhook configuration
  2. In Gitea, go to repo Settings → Webhooks
  3. Verify webhook URL and secret match
  4. 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:

  1. Push changes to dev branch
  2. Create 3 Dokploy applications (dev, staging, prod)
  3. Configure webhooks for each branch
  4. Deploy and test each environment

Questions? Check the main README.md or CLAUDE.md for more details.