Merge dev into staging - resolve docker-compose.local.yml conflict
This commit is contained in:
@@ -13,6 +13,7 @@ node_modules
|
|||||||
# Documentation
|
# Documentation
|
||||||
*.md
|
*.md
|
||||||
!README.md
|
!README.md
|
||||||
|
docs
|
||||||
|
|
||||||
# IDE
|
# IDE
|
||||||
.vscode
|
.vscode
|
||||||
@@ -49,6 +50,7 @@ docker-compose*.yml
|
|||||||
# CI/CD
|
# CI/CD
|
||||||
.github
|
.github
|
||||||
.gitlab-ci.yml
|
.gitlab-ci.yml
|
||||||
|
.gitea
|
||||||
|
|
||||||
# Scripts
|
# Scripts
|
||||||
scripts
|
scripts
|
||||||
|
|||||||
57
.gitea/workflows/docker-publish-dev.yaml
Normal file
57
.gitea/workflows/docker-publish-dev.yaml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
name: Build and Push Docker Image (Dev)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- 'src/**'
|
||||||
|
- 'client/**'
|
||||||
|
- 'Dockerfile'
|
||||||
|
- 'docker-compose.dev.yml'
|
||||||
|
- 'package.json'
|
||||||
|
- '.gitea/workflows/docker-publish-dev.yaml'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: git.app.flexinit.nl
|
||||||
|
IMAGE_NAME: oussamadouhou/ai-stack-deployer
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push-dev:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to Gitea Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: oussamadouhou
|
||||||
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
tags: |
|
||||||
|
type=raw,value=dev
|
||||||
|
type=sha,prefix=dev-
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Build and Push Docker Image
|
name: Build and Push Docker Image (Production)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -6,8 +6,11 @@ on:
|
|||||||
- main
|
- main
|
||||||
paths:
|
paths:
|
||||||
- 'src/**'
|
- 'src/**'
|
||||||
|
- 'client/**'
|
||||||
- 'Dockerfile'
|
- 'Dockerfile'
|
||||||
- '.gitea/workflows/**'
|
- 'docker-compose.prod.yml'
|
||||||
|
- 'package.json'
|
||||||
|
- '.gitea/workflows/docker-publish-main.yaml'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@@ -15,7 +18,7 @@ env:
|
|||||||
IMAGE_NAME: oussamadouhou/ai-stack-deployer
|
IMAGE_NAME: oussamadouhou/ai-stack-deployer
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-push:
|
build-and-push-main:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -41,8 +44,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,value=latest,enable={{is_default_branch}}
|
type=raw,value=latest
|
||||||
type=sha,prefix=
|
type=sha,prefix=main-
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
57
.gitea/workflows/docker-publish-staging.yaml
Normal file
57
.gitea/workflows/docker-publish-staging.yaml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
name: Build and Push Docker Image (Staging)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- staging
|
||||||
|
paths:
|
||||||
|
- 'src/**'
|
||||||
|
- 'client/**'
|
||||||
|
- 'Dockerfile'
|
||||||
|
- 'docker-compose.staging.yml'
|
||||||
|
- 'package.json'
|
||||||
|
- '.gitea/workflows/docker-publish-staging.yaml'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: git.app.flexinit.nl
|
||||||
|
IMAGE_NAME: oussamadouhou/ai-stack-deployer
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push-staging:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to Gitea Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: oussamadouhou
|
||||||
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
tags: |
|
||||||
|
type=raw,value=staging
|
||||||
|
type=sha,prefix=staging-
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
@@ -316,6 +316,13 @@ Missing (needs implementation):
|
|||||||
|
|
||||||
### Docker Build and Run
|
### Docker Build and Run
|
||||||
|
|
||||||
|
**Build Architecture**: The Dockerfile uses a hybrid approach to avoid AVX CPU requirements:
|
||||||
|
|
||||||
|
- **Build stage** (Node.js 20): Builds React client with Vite (no AVX required)
|
||||||
|
- **Runtime stage** (Bun 1.3): Runs the API server (Bun only needs AVX for builds, not runtime)
|
||||||
|
|
||||||
|
This approach ensures the Docker image builds successfully on all CPU architectures, including older systems and some cloud build environments that lack AVX support.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build the Docker image
|
# Build the Docker image
|
||||||
docker build -t ai-stack-deployer:latest .
|
docker build -t ai-stack-deployer:latest .
|
||||||
@@ -331,6 +338,8 @@ docker run -d \
|
|||||||
ai-stack-deployer:latest
|
ai-stack-deployer:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Note**: If you encounter "CPU lacks AVX support" errors during Docker builds, ensure you're using the latest Dockerfile which implements the Node.js/Bun hybrid build strategy.
|
||||||
|
|
||||||
### Deploying to Dokploy
|
### Deploying to Dokploy
|
||||||
|
|
||||||
1. **Prepare Environment**:
|
1. **Prepare Environment**:
|
||||||
|
|||||||
25
Dockerfile
25
Dockerfile
@@ -1,22 +1,25 @@
|
|||||||
# Use official Bun image
|
|
||||||
# ***NEVER FORGET THE PRINCIPLES RULES***
|
# ***NEVER FORGET THE PRINCIPLES RULES***
|
||||||
FROM oven/bun:1.3-alpine AS base
|
|
||||||
|
|
||||||
# Set working directory
|
# Build stage - Use Node.js to avoid AVX CPU requirement
|
||||||
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copy package files
|
|
||||||
COPY package.json bun.lock* ./
|
COPY package.json bun.lock* ./
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies using npm (works without AVX)
|
||||||
FROM base AS deps
|
RUN npm install
|
||||||
RUN bun install --frozen-lockfile --production
|
|
||||||
|
|
||||||
# Build stage
|
|
||||||
FROM base AS builder
|
|
||||||
RUN bun install --frozen-lockfile
|
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN bun run build
|
|
||||||
|
# Client: Vite build via Node.js
|
||||||
|
# API: Skip bun build, copy src files directly (Bun will run them at runtime)
|
||||||
|
RUN npm run build:client
|
||||||
|
|
||||||
|
FROM node:20-alpine AS deps
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json bun.lock* ./
|
||||||
|
RUN npm install --production
|
||||||
|
|
||||||
# Production stage
|
# Production stage
|
||||||
FROM oven/bun:1.3-alpine AS runner
|
FROM oven/bun:1.3-alpine AS runner
|
||||||
|
|||||||
25
README.md
25
README.md
@@ -36,13 +36,16 @@ User's AI Stack Container (OpenCode + ttyd)
|
|||||||
|
|
||||||
### Technology Stack
|
### Technology Stack
|
||||||
|
|
||||||
- **Runtime**: Bun 1.3+
|
- **Runtime**: Bun 1.3+ (production), Node.js 20 (build)
|
||||||
- **Framework**: Hono 4.11.3
|
- **Framework**: Hono 4.11.3
|
||||||
- **Language**: TypeScript
|
- **Language**: TypeScript
|
||||||
- **Container**: Docker with multi-stage builds
|
- **Frontend**: React 19 + Vite + Tailwind CSS 4
|
||||||
|
- **Container**: Docker with multi-stage builds (Node.js build, Bun runtime)
|
||||||
- **Orchestration**: Dokploy
|
- **Orchestration**: Dokploy
|
||||||
- **Reverse Proxy**: Traefik with wildcard SSL
|
- **Reverse Proxy**: Traefik with wildcard SSL
|
||||||
|
|
||||||
|
**Build Strategy**: Uses Node.js for building (avoids AVX CPU requirement) and Bun for runtime (performance).
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
@@ -344,6 +347,24 @@ If a deployment fails but the name is marked as taken:
|
|||||||
2. Delete the partial deployment if present
|
2. Delete the partial deployment if present
|
||||||
3. Try deployment again
|
3. Try deployment again
|
||||||
|
|
||||||
|
### Docker Build Fails with "CPU lacks AVX support"
|
||||||
|
|
||||||
|
**Error**: `panic(main thread): Illegal instruction at address 0x...`
|
||||||
|
|
||||||
|
**Cause**: Bun requires AVX CPU instructions which may not be available in all Docker build environments.
|
||||||
|
|
||||||
|
**Solution**: Already implemented in Dockerfile. The build uses Node.js (no AVX requirement) for building and Bun for runtime:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM node:20-alpine AS builder
|
||||||
|
RUN npm install
|
||||||
|
RUN npm run build:client
|
||||||
|
|
||||||
|
FROM oven/bun:1.3-alpine AS runner
|
||||||
|
```
|
||||||
|
|
||||||
|
If you see this error, ensure you're using the latest Dockerfile from the repository.
|
||||||
|
|
||||||
## Security Notes
|
## Security Notes
|
||||||
|
|
||||||
- All API tokens stored in environment variables (never in code)
|
- All API tokens stored in environment variables (never in code)
|
||||||
|
|||||||
36
docker-compose.dev.yml
Normal file
36
docker-compose.dev.yml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
services:
|
||||||
|
ai-stack-deployer:
|
||||||
|
image: git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:dev
|
||||||
|
container_name: ai-stack-deployer-dev
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=development
|
||||||
|
- PORT=3000
|
||||||
|
- HOST=0.0.0.0
|
||||||
|
- DOKPLOY_URL=${DOKPLOY_URL}
|
||||||
|
- DOKPLOY_API_TOKEN=${DOKPLOY_API_TOKEN}
|
||||||
|
- STACK_DOMAIN_SUFFIX=${STACK_DOMAIN_SUFFIX:-ai.flexinit.nl}
|
||||||
|
- STACK_IMAGE=${STACK_IMAGE:-git.app.flexinit.nl/flexinit/agent-stack:latest}
|
||||||
|
- RESERVED_NAMES=${RESERVED_NAMES:-admin,api,www,root,system,test,demo,portal}
|
||||||
|
- SHARED_PROJECT_ID=$${{project.SHARED_PROJECT_ID}}
|
||||||
|
- SHARED_ENVIRONMENT_ID=$${{project.SHARED_ENVIRONMENT_ID}}
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"bun",
|
||||||
|
"--eval",
|
||||||
|
"fetch('http://localhost:3000/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))",
|
||||||
|
]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 3
|
||||||
|
start_period: 5s
|
||||||
|
networks:
|
||||||
|
- ai-stack-network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
ai-stack-network:
|
||||||
|
driver: bridge
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
version: "3.8"
|
version: "3.8"
|
||||||
|
|
||||||
# ***NEVER FORGET THE PRINCIPLES***
|
|
||||||
services:
|
services:
|
||||||
ai-stack-deployer:
|
ai-stack-deployer:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
container_name: ai-stack-deployer
|
container_name: ai-stack-deployer-local
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=development
|
||||||
- PORT=3000
|
- PORT=3000
|
||||||
- HOST=0.0.0.0
|
- HOST=0.0.0.0
|
||||||
- DOKPLOY_URL=${DOKPLOY_URL}
|
- DOKPLOY_URL=${DOKPLOY_URL}
|
||||||
@@ -33,6 +32,9 @@ services:
|
|||||||
start_period: 5s
|
start_period: 5s
|
||||||
networks:
|
networks:
|
||||||
- ai-stack-network
|
- ai-stack-network
|
||||||
|
volumes:
|
||||||
|
- ./src:/app/src:ro
|
||||||
|
- ./client:/app/client:ro
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
ai-stack-network:
|
ai-stack-network:
|
||||||
38
docker-compose.prod.yml
Normal file
38
docker-compose.prod.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
ai-stack-deployer:
|
||||||
|
image: git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:latest
|
||||||
|
container_name: ai-stack-deployer
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- PORT=3000
|
||||||
|
- HOST=0.0.0.0
|
||||||
|
- DOKPLOY_URL=${DOKPLOY_URL}
|
||||||
|
- DOKPLOY_API_TOKEN=${DOKPLOY_API_TOKEN}
|
||||||
|
- STACK_DOMAIN_SUFFIX=${STACK_DOMAIN_SUFFIX:-ai.flexinit.nl}
|
||||||
|
- STACK_IMAGE=${STACK_IMAGE:-git.app.flexinit.nl/flexinit/agent-stack:latest}
|
||||||
|
- RESERVED_NAMES=${RESERVED_NAMES:-admin,api,www,root,system,test,demo,portal}
|
||||||
|
- SHARED_PROJECT_ID=$${{project.SHARED_PROJECT_ID}}
|
||||||
|
- SHARED_ENVIRONMENT_ID=$${{project.SHARED_ENVIRONMENT_ID}}
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"bun",
|
||||||
|
"--eval",
|
||||||
|
"fetch('http://localhost:3000/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))",
|
||||||
|
]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 3
|
||||||
|
start_period: 5s
|
||||||
|
networks:
|
||||||
|
- ai-stack-network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
ai-stack-network:
|
||||||
|
driver: bridge
|
||||||
38
docker-compose.staging.yml
Normal file
38
docker-compose.staging.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
ai-stack-deployer:
|
||||||
|
image: git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:staging
|
||||||
|
container_name: ai-stack-deployer-staging
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=staging
|
||||||
|
- PORT=3000
|
||||||
|
- HOST=0.0.0.0
|
||||||
|
- DOKPLOY_URL=${DOKPLOY_URL}
|
||||||
|
- DOKPLOY_API_TOKEN=${DOKPLOY_API_TOKEN}
|
||||||
|
- STACK_DOMAIN_SUFFIX=${STACK_DOMAIN_SUFFIX:-ai.flexinit.nl}
|
||||||
|
- STACK_IMAGE=${STACK_IMAGE:-git.app.flexinit.nl/flexinit/agent-stack:latest}
|
||||||
|
- RESERVED_NAMES=${RESERVED_NAMES:-admin,api,www,root,system,test,demo,portal}
|
||||||
|
- SHARED_PROJECT_ID=$${{project.SHARED_PROJECT_ID}}
|
||||||
|
- SHARED_ENVIRONMENT_ID=$${{project.SHARED_ENVIRONMENT_ID}}
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"bun",
|
||||||
|
"--eval",
|
||||||
|
"fetch('http://localhost:3000/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))",
|
||||||
|
]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 3
|
||||||
|
start_period: 5s
|
||||||
|
networks:
|
||||||
|
- ai-stack-network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
ai-stack-network:
|
||||||
|
driver: bridge
|
||||||
198
docs/DOCKER_BUILD_FIX.md
Normal file
198
docs/DOCKER_BUILD_FIX.md
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
# Docker Build AVX Fix
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
Docker build was failing with:
|
||||||
|
```
|
||||||
|
CPU lacks AVX support. Please consider upgrading to a newer CPU.
|
||||||
|
panic(main thread): Illegal instruction at address 0x3F3EDB4
|
||||||
|
oh no: Bun has crashed. This indicates a bug in Bun, not your code.
|
||||||
|
error: script "build:client" was terminated by signal SIGILL (Illegal instruction)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Root Cause
|
||||||
|
|
||||||
|
Bun requires **AVX (Advanced Vector Extensions)** CPU instructions for its build operations. Many Docker build environments, especially:
|
||||||
|
- Older CPUs
|
||||||
|
- Some cloud CI/CD systems
|
||||||
|
- Virtual machines with limited CPU feature passthrough
|
||||||
|
|
||||||
|
...do not provide AVX support, causing Bun to crash with "Illegal instruction" errors.
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
Implemented a **hybrid build strategy** in the Dockerfile:
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Build stage - Use Node.js to avoid AVX CPU requirement
|
||||||
|
FROM node:20-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json bun.lock* ./
|
||||||
|
RUN npm install
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build:client
|
||||||
|
|
||||||
|
# Production dependencies
|
||||||
|
FROM node:20-alpine AS deps
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json bun.lock* ./
|
||||||
|
RUN npm install --production
|
||||||
|
|
||||||
|
# Runtime stage - Use Bun for running the app
|
||||||
|
FROM oven/bun:1.3-alpine AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY --from=builder /app/src ./src
|
||||||
|
COPY --from=builder /app/dist/client ./dist/client
|
||||||
|
COPY --from=builder /app/package.json ./
|
||||||
|
CMD ["bun", "run", "start"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Why This Works
|
||||||
|
|
||||||
|
1. **Build Phase (Node.js)**:
|
||||||
|
- Vite (used for React build) runs on Node.js without AVX requirement
|
||||||
|
- `npm install` and `npm run build:client` work on all CPU architectures
|
||||||
|
- Builds the React client to `dist/client/`
|
||||||
|
|
||||||
|
2. **Runtime Phase (Bun)**:
|
||||||
|
- Bun **does NOT require AVX for running TypeScript files**
|
||||||
|
- Only needs AVX for build operations (which we avoid)
|
||||||
|
- Provides better performance at runtime compared to Node.js
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
✅ **Universal Compatibility**: Builds on all CPU architectures
|
||||||
|
✅ **No Performance Loss**: Bun still used for runtime (faster than Node.js)
|
||||||
|
✅ **Clean Separation**: Build tools vs. runtime environment
|
||||||
|
✅ **Production Ready**: Tested and verified working
|
||||||
|
|
||||||
|
## Test Results
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build successful
|
||||||
|
docker build -t ai-stack-deployer:test .
|
||||||
|
Successfully built 1811daf55502
|
||||||
|
|
||||||
|
# Container runs correctly
|
||||||
|
docker run -d --name test -p 3001:3000 ai-stack-deployer:test
|
||||||
|
Container ID: 7c4acbf49737
|
||||||
|
|
||||||
|
# Health check passes
|
||||||
|
curl http://localhost:3001/health
|
||||||
|
{
|
||||||
|
"status": "healthy",
|
||||||
|
"version": "0.2.0",
|
||||||
|
"service": "ai-stack-deployer",
|
||||||
|
"features": {
|
||||||
|
"productionClient": true,
|
||||||
|
"retryLogic": true,
|
||||||
|
"circuitBreaker": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# React client serves correctly
|
||||||
|
curl http://localhost:3001/
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<script type="module" crossorigin src="/assets/index-kibXed5Q.js"></script>
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Date
|
||||||
|
|
||||||
|
**Date**: January 13, 2026
|
||||||
|
**Branch**: dev (following Git Flow)
|
||||||
|
**Files Modified**:
|
||||||
|
- `Dockerfile` - Switched build stage from Bun to Node.js
|
||||||
|
- `README.md` - Updated Technology Stack and Troubleshooting sections
|
||||||
|
- `CLAUDE.md` - Documented Docker build architecture
|
||||||
|
|
||||||
|
## Alternative Solutions Considered
|
||||||
|
|
||||||
|
### ❌ Option 1: Use Debian-based Bun image
|
||||||
|
```dockerfile
|
||||||
|
FROM oven/bun:1.3-debian
|
||||||
|
```
|
||||||
|
**Rejected**: Debian images are larger (~200MB vs ~50MB Alpine), and still require AVX support.
|
||||||
|
|
||||||
|
### ❌ Option 2: Use older Bun version
|
||||||
|
```dockerfile
|
||||||
|
FROM oven/bun:1.0-alpine
|
||||||
|
```
|
||||||
|
**Rejected**: Loses new features, security patches, and performance improvements.
|
||||||
|
|
||||||
|
### ❌ Option 3: Build locally and commit dist/
|
||||||
|
```bash
|
||||||
|
bun run build:client
|
||||||
|
git add dist/client/
|
||||||
|
```
|
||||||
|
**Rejected**: Build artifacts shouldn't be in source control. Makes CI/CD harder.
|
||||||
|
|
||||||
|
### ✅ Option 4: Hybrid Node.js/Bun strategy (CHOSEN)
|
||||||
|
**Why**: Best of both worlds - universal build compatibility + Bun runtime performance.
|
||||||
|
|
||||||
|
## Future Considerations
|
||||||
|
|
||||||
|
If Bun removes AVX requirement in future versions, we could:
|
||||||
|
1. Simplify Dockerfile back to single Bun stage
|
||||||
|
2. Keep current approach for maximum compatibility
|
||||||
|
3. Monitor Bun release notes for AVX-related changes
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- Bun Issue #1521: AVX requirement discussion
|
||||||
|
- Docker Multi-stage builds: https://docs.docker.com/build/building/multi-stage/
|
||||||
|
- Vite Documentation: https://vitejs.dev/guide/build.html
|
||||||
|
|
||||||
|
## Verification Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clean build test
|
||||||
|
docker build --no-cache -t ai-stack-deployer:test .
|
||||||
|
|
||||||
|
# Run and verify
|
||||||
|
docker run -d --name test -p 3001:3000 -e DOKPLOY_API_TOKEN=test ai-stack-deployer:test
|
||||||
|
sleep 3
|
||||||
|
curl http://localhost:3001/health | jq .
|
||||||
|
docker logs test
|
||||||
|
docker stop test && docker rm test
|
||||||
|
|
||||||
|
# Production build
|
||||||
|
docker build -t ai-stack-deployer:latest .
|
||||||
|
docker-compose up -d
|
||||||
|
docker-compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
If you still encounter AVX errors:
|
||||||
|
|
||||||
|
1. **Verify you're using the latest Dockerfile**:
|
||||||
|
```bash
|
||||||
|
git pull origin dev
|
||||||
|
head -10 Dockerfile
|
||||||
|
# Should show: FROM node:20-alpine AS builder
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Clear Docker build cache**:
|
||||||
|
```bash
|
||||||
|
docker builder prune -a
|
||||||
|
docker build --no-cache -t ai-stack-deployer:latest .
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Check Docker version**:
|
||||||
|
```bash
|
||||||
|
docker --version
|
||||||
|
# Recommended: Docker 20.10+ with BuildKit
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
For issues or questions about this fix, refer to:
|
||||||
|
- `CLAUDE.md` - Development guidelines
|
||||||
|
- `README.md` - Troubleshooting section
|
||||||
|
- Docker logs: `docker-compose logs -f`
|
||||||
488
docs/DOKPLOY_DEPLOYMENT.md
Normal file
488
docs/DOKPLOY_DEPLOYMENT.md
Normal file
@@ -0,0 +1,488 @@
|
|||||||
|
# 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**:
|
||||||
|
```yaml
|
||||||
|
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**:
|
||||||
|
```bash
|
||||||
|
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:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
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"
|
||||||
|
|
||||||
|
**Note**: The double `$$` is required to escape the dollar sign in Docker Compose files.
|
||||||
|
|
||||||
|
### 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:
|
||||||
|
```env
|
||||||
|
SHARED_PROJECT_ID=<your-shared-project-id>
|
||||||
|
SHARED_ENVIRONMENT_ID=<your-shared-environment-id>
|
||||||
|
```
|
||||||
|
|
||||||
|
And these **application-level variables** in the portal app:
|
||||||
|
```env
|
||||||
|
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:
|
||||||
|
```yaml
|
||||||
|
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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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:
|
||||||
|
```yaml
|
||||||
|
image: git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:main-abc1234
|
||||||
|
```
|
||||||
|
4. **Redeploy**
|
||||||
|
|
||||||
|
### Manual Rollback via Git
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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
|
||||||
|
|
||||||
|
```env
|
||||||
|
DOKPLOY_URL=http://10.100.0.20:3000
|
||||||
|
DOKPLOY_API_TOKEN=<your-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Optional (with defaults)
|
||||||
|
|
||||||
|
```env
|
||||||
|
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**:
|
||||||
|
```env
|
||||||
|
STACK_DOMAIN_SUFFIX=dev-ai.flexinit.nl
|
||||||
|
```
|
||||||
|
|
||||||
|
**Staging**:
|
||||||
|
```env
|
||||||
|
STACK_DOMAIN_SUFFIX=staging-ai.flexinit.nl
|
||||||
|
```
|
||||||
|
|
||||||
|
**Prod**:
|
||||||
|
```env
|
||||||
|
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**:
|
||||||
|
```bash
|
||||||
|
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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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.
|
||||||
313
docs/SHARED_PROJECT_DEPLOYMENT.md
Normal file
313
docs/SHARED_PROJECT_DEPLOYMENT.md
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
# Shared Project Deployment Architecture
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The AI Stack Deployer portal deploys **all user AI stacks to a single shared Dokploy project** instead of creating a new project for each user.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture Diagram
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Dokploy: ai-stack-portal (Shared Project) │
|
||||||
|
│ ID: 2y2Glhz5Wy0dBNf6BOR_- │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ 📦 Portal Application: ai-stack-deployer-prod │
|
||||||
|
│ ├─ Domain: portal.ai.flexinit.nl │
|
||||||
|
│ ├─ Image: git.app.flexinit.nl/.../ai-stack-deployer:latest│
|
||||||
|
│ └─ Env: SHARED_PROJECT_ID=$${{project.SHARED_PROJECT_ID}} │
|
||||||
|
│ │
|
||||||
|
│ ───────────────────────────────────────────────────────────── │
|
||||||
|
│ │
|
||||||
|
│ 📦 User Stack: john-dev │
|
||||||
|
│ ├─ Domain: john-dev.ai.flexinit.nl │
|
||||||
|
│ ├─ Image: git.app.flexinit.nl/.../agent-stack:latest │
|
||||||
|
│ └─ Deployed by: Portal │
|
||||||
|
│ │
|
||||||
|
│ 📦 User Stack: jane-prod │
|
||||||
|
│ ├─ Domain: jane-prod.ai.flexinit.nl │
|
||||||
|
│ ├─ Image: git.app.flexinit.nl/.../agent-stack:latest │
|
||||||
|
│ └─ Deployed by: Portal │
|
||||||
|
│ │
|
||||||
|
│ 📦 User Stack: alice-test │
|
||||||
|
│ ├─ Domain: alice-test.ai.flexinit.nl │
|
||||||
|
│ ├─ Image: git.app.flexinit.nl/.../agent-stack:latest │
|
||||||
|
│ └─ Deployed by: Portal │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### Step 1: Portal Reads Configuration
|
||||||
|
|
||||||
|
When a user submits a stack name (e.g., "john-dev"), the portal:
|
||||||
|
|
||||||
|
1. **Reads environment variables**:
|
||||||
|
```javascript
|
||||||
|
const sharedProjectId = process.env.SHARED_PROJECT_ID;
|
||||||
|
const sharedEnvironmentId = process.env.SHARED_ENVIRONMENT_ID;
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **These are set via Dokploy's project-level variables**:
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
- SHARED_PROJECT_ID=$${{project.SHARED_PROJECT_ID}}
|
||||||
|
- SHARED_ENVIRONMENT_ID=$${{project.SHARED_ENVIRONMENT_ID}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: The double `$$` is required to escape the dollar sign in Docker Compose.
|
||||||
|
|
||||||
|
### Step 2: Portal Deploys to Shared Project
|
||||||
|
|
||||||
|
Instead of creating a new project, the portal:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// OLD BEHAVIOR (legacy):
|
||||||
|
// createProject(`ai-stack-${username}`) ❌ Creates new project per user
|
||||||
|
|
||||||
|
// NEW BEHAVIOR (current):
|
||||||
|
// Uses existing shared project ID ✅
|
||||||
|
const projectId = sharedProjectId; // From environment variable
|
||||||
|
const environmentId = sharedEnvironmentId;
|
||||||
|
|
||||||
|
// Creates application IN the shared project
|
||||||
|
createApplication({
|
||||||
|
projectId: projectId,
|
||||||
|
environmentId: environmentId,
|
||||||
|
name: `${username}-stack`,
|
||||||
|
image: 'git.app.flexinit.nl/.../agent-stack:latest',
|
||||||
|
domain: `${username}.ai.flexinit.nl`
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: User Accesses Their Stack
|
||||||
|
|
||||||
|
User visits `https://john-dev.ai.flexinit.nl` → Traefik routes to their application inside the shared project.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration Steps
|
||||||
|
|
||||||
|
### 1. Create Shared Project in Dokploy
|
||||||
|
|
||||||
|
1. In Dokploy UI, create project:
|
||||||
|
- **Name**: `ai-stack-portal`
|
||||||
|
- **Description**: "Shared project for all user AI stacks"
|
||||||
|
|
||||||
|
2. Get the **Project ID**:
|
||||||
|
```bash
|
||||||
|
# Via API
|
||||||
|
curl -s "http://10.100.0.20:3000/api/project.all" \
|
||||||
|
-H "Authorization: Bearer $DOKPLOY_API_TOKEN" | \
|
||||||
|
jq -r '.[] | select(.name=="ai-stack-portal") | .id'
|
||||||
|
|
||||||
|
# Output: 2y2Glhz5Wy0dBNf6BOR_-
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Get the **Environment ID**:
|
||||||
|
```bash
|
||||||
|
curl -s "http://10.100.0.20:3000/api/project.one?projectId=2y2Glhz5Wy0dBNf6BOR_-" \
|
||||||
|
-H "Authorization: Bearer $DOKPLOY_API_TOKEN" | \
|
||||||
|
jq -r '.environments[0].id'
|
||||||
|
|
||||||
|
# Output: RqE9OFMdLwkzN7pif1xN8
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Set Project-Level Variables
|
||||||
|
|
||||||
|
In the shared project (`ai-stack-portal`), add these **project-level environment variables**:
|
||||||
|
|
||||||
|
| Variable | Value | Example |
|
||||||
|
|----------|-------|---------|
|
||||||
|
| `SHARED_PROJECT_ID` | Your project ID | `2y2Glhz5Wy0dBNf6BOR_-` |
|
||||||
|
| `SHARED_ENVIRONMENT_ID` | Your environment ID | `RqE9OFMdLwkzN7pif1xN8` |
|
||||||
|
|
||||||
|
**How to set in Dokploy UI**:
|
||||||
|
- Go to Project → Settings → Environment Variables
|
||||||
|
- Add variables at **project level** (not application level)
|
||||||
|
|
||||||
|
### 3. Deploy Portal Application
|
||||||
|
|
||||||
|
Deploy the portal **inside the same shared project**:
|
||||||
|
|
||||||
|
1. **Application Details**:
|
||||||
|
- Name: `ai-stack-deployer-prod`
|
||||||
|
- Type: Docker Compose
|
||||||
|
- Compose File: `docker-compose.prod.yml`
|
||||||
|
- Branch: `main`
|
||||||
|
|
||||||
|
2. **The docker-compose file automatically references project variables**:
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
- SHARED_PROJECT_ID=${{project.SHARED_PROJECT_ID}} # ← Magic happens here
|
||||||
|
- SHARED_ENVIRONMENT_ID=${{project.SHARED_ENVIRONMENT_ID}}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Dokploy resolves `${{project.VAR}}`** to the actual value from project-level variables.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
### ✅ Centralized Management
|
||||||
|
All user stacks in one place:
|
||||||
|
- Easy to list all active stacks
|
||||||
|
- Shared monitoring dashboard
|
||||||
|
- Centralized logging
|
||||||
|
|
||||||
|
### ✅ Resource Efficiency
|
||||||
|
- No overhead of separate projects per user
|
||||||
|
- Shared network and resources
|
||||||
|
- Easier to manage quotas
|
||||||
|
|
||||||
|
### ✅ Simplified Configuration
|
||||||
|
- Project-level environment variables shared by all stacks
|
||||||
|
- Single source of truth for common configs
|
||||||
|
- Easy to update STACK_IMAGE for all users
|
||||||
|
|
||||||
|
### ✅ Better Organization
|
||||||
|
```
|
||||||
|
Projects in Dokploy:
|
||||||
|
├── ai-stack-portal (500 user applications) ✅ Clean
|
||||||
|
└── NOT:
|
||||||
|
├── ai-stack-john
|
||||||
|
├── ai-stack-jane
|
||||||
|
├── ai-stack-alice
|
||||||
|
└── ... (500 separate projects) ❌ Messy
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fallback Behavior
|
||||||
|
|
||||||
|
If `SHARED_PROJECT_ID` and `SHARED_ENVIRONMENT_ID` are **not set**, the portal falls back to **legacy behavior**:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Code in src/orchestrator/production-deployer.ts (lines 187-196)
|
||||||
|
const sharedProjectId = config.sharedProjectId || process.env.SHARED_PROJECT_ID;
|
||||||
|
const sharedEnvironmentId = config.sharedEnvironmentId || process.env.SHARED_ENVIRONMENT_ID;
|
||||||
|
|
||||||
|
if (sharedProjectId && sharedEnvironmentId) {
|
||||||
|
// Use shared project ✅
|
||||||
|
state.resources.projectId = sharedProjectId;
|
||||||
|
state.resources.environmentId = sharedEnvironmentId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: Create separate project per user ⚠️
|
||||||
|
const projectName = `ai-stack-${config.stackName}`;
|
||||||
|
const existingProject = await this.client.findProjectByName(projectName);
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
**This ensures backwards compatibility** but is not recommended.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Portal Creates Separate Projects Instead of Using Shared Project
|
||||||
|
|
||||||
|
**Cause**: `SHARED_PROJECT_ID` or `SHARED_ENVIRONMENT_ID` not set.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. Check project-level variables in Dokploy:
|
||||||
|
```bash
|
||||||
|
curl -s "http://10.100.0.20:3000/api/project.one?projectId=YOUR_PROJECT_ID" \
|
||||||
|
-H "Authorization: Bearer $DOKPLOY_API_TOKEN" | \
|
||||||
|
jq '.environmentVariables'
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Ensure the portal application's docker-compose references them:
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
- SHARED_PROJECT_ID=${{project.SHARED_PROJECT_ID}}
|
||||||
|
- SHARED_ENVIRONMENT_ID=${{project.SHARED_ENVIRONMENT_ID}}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Redeploy the portal application.
|
||||||
|
|
||||||
|
### Variable Reference Not Working
|
||||||
|
|
||||||
|
**Symptom**: Portal logs show `undefined` for `SHARED_PROJECT_ID`.
|
||||||
|
|
||||||
|
**Cause**: Using wrong syntax.
|
||||||
|
|
||||||
|
**Correct syntax**:
|
||||||
|
```yaml
|
||||||
|
- SHARED_PROJECT_ID=${{project.SHARED_PROJECT_ID}} ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wrong syntax**:
|
||||||
|
```yaml
|
||||||
|
- SHARED_PROJECT_ID=${SHARED_PROJECT_ID} ❌ (shell substitution, not Dokploy)
|
||||||
|
- SHARED_PROJECT_ID={{project.SHARED_PROJECT_ID}} ❌ (missing $)
|
||||||
|
```
|
||||||
|
|
||||||
|
### How to Verify Configuration
|
||||||
|
|
||||||
|
Check portal container environment:
|
||||||
|
```bash
|
||||||
|
# SSH into Dokploy host
|
||||||
|
ssh user@10.100.0.20
|
||||||
|
|
||||||
|
# Inspect portal container
|
||||||
|
docker exec ai-stack-deployer env | grep SHARED
|
||||||
|
|
||||||
|
# Should show:
|
||||||
|
SHARED_PROJECT_ID=2y2Glhz5Wy0dBNf6BOR_-
|
||||||
|
SHARED_ENVIRONMENT_ID=RqE9OFMdLwkzN7pif1xN8
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Environment-Specific Shared Projects
|
||||||
|
|
||||||
|
You can have **separate shared projects for dev/staging/prod**:
|
||||||
|
|
||||||
|
| Portal Environment | Shared Project | Purpose |
|
||||||
|
|--------------------|----------------|---------|
|
||||||
|
| Dev | `ai-stack-portal-dev` | Development user stacks |
|
||||||
|
| Staging | `ai-stack-portal-staging` | Staging user stacks |
|
||||||
|
| Prod | `ai-stack-portal` | Production user stacks |
|
||||||
|
|
||||||
|
Each portal deployment references its own shared project:
|
||||||
|
- `portal-dev.ai.flexinit.nl` → `ai-stack-portal-dev`
|
||||||
|
- `portal-staging.ai.flexinit.nl` → `ai-stack-portal-staging`
|
||||||
|
- `portal.ai.flexinit.nl` → `ai-stack-portal`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Migration from Legacy
|
||||||
|
|
||||||
|
If you're currently using the legacy behavior (separate projects per user):
|
||||||
|
|
||||||
|
### Option 1: Gradual Migration
|
||||||
|
- New deployments use shared project
|
||||||
|
- Old deployments remain in separate projects
|
||||||
|
- Migrate old stacks manually over time
|
||||||
|
|
||||||
|
### Option 2: Full Migration
|
||||||
|
1. Create shared project
|
||||||
|
2. Set project-level variables
|
||||||
|
3. Redeploy all user stacks to shared project
|
||||||
|
4. Delete old separate projects
|
||||||
|
|
||||||
|
**Note**: Migration requires downtime for each stack being moved.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
- **Environment Variable Syntax**: See Dokploy docs on project-level variables
|
||||||
|
- **Code Location**: `src/orchestrator/production-deployer.ts` (lines 178-200)
|
||||||
|
- **Example IDs**: `.env.example` (lines 25-27)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Questions?** Check the main deployment guide: `DOKPLOY_DEPLOYMENT.md`
|
||||||
110
docs/TESTING.md
110
docs/TESTING.md
@@ -261,3 +261,113 @@ Authorization: token <your-api-token>
|
|||||||
|-----|---------|
|
|-----|---------|
|
||||||
| `GITEA_API_TOKEN` | Gitea API access for workflow status |
|
| `GITEA_API_TOKEN` | Gitea API access for workflow status |
|
||||||
| `DOKPLOY_API_TOKEN` | Dokploy deployment API (BWS ID: `6b3618fc-ba02-49bc-bdc8-b3c9004087bc`) |
|
| `DOKPLOY_API_TOKEN` | Dokploy deployment API (BWS ID: `6b3618fc-ba02-49bc-bdc8-b3c9004087bc`) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Session: 2026-01-13
|
||||||
|
|
||||||
|
### Session Summary
|
||||||
|
|
||||||
|
**Goal:** Verify multi-environment deployment setup and shared project configuration.
|
||||||
|
|
||||||
|
### Completed Tasks
|
||||||
|
|
||||||
|
| Task | Status | Evidence |
|
||||||
|
|------|--------|----------|
|
||||||
|
| Workflow separation (dev/staging/main) | ✅ | Committed as `eb2745d` |
|
||||||
|
| Dollar sign escaping (`$${{project.VAR}}`) | ✅ | Verified in all docker-compose.*.yml |
|
||||||
|
| Shared project exists | ✅ | `ai-stack-portal` (ID: `2y2Glhz5Wy0dBNf6BOR_-`) |
|
||||||
|
| Environment IDs retrieved | ✅ | See below |
|
||||||
|
| Local dev server health | ✅ | `/health` returns healthy |
|
||||||
|
|
||||||
|
### Environment IDs
|
||||||
|
|
||||||
|
```
|
||||||
|
Project: ai-stack-portal
|
||||||
|
ID: 2y2Glhz5Wy0dBNf6BOR_-
|
||||||
|
|
||||||
|
Environments:
|
||||||
|
- production: _dKAmxVcadqi-z73wKpEB (default)
|
||||||
|
- deployments: RqE9OFMdLwkzN7pif1xN8 (for user stacks)
|
||||||
|
- test: KVKn5fXGz10g7KVxPWOQj
|
||||||
|
```
|
||||||
|
|
||||||
|
### Blockers Identified
|
||||||
|
|
||||||
|
#### BLOCKER: Dokploy API Token Permissions
|
||||||
|
|
||||||
|
**Symptom:** All Dokploy API calls return `Forbidden`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Previously working
|
||||||
|
curl -s "https://app.flexinit.nl/api/project.all" -H "x-api-key: $DOKPLOY_API_TOKEN"
|
||||||
|
# Now returns: Forbidden
|
||||||
|
|
||||||
|
# Environment endpoint
|
||||||
|
curl -s "https://app.flexinit.nl/api/environment.one?environmentId=RqE9OFMdLwkzN7pif1xN8" -H "x-api-key: $DOKPLOY_API_TOKEN"
|
||||||
|
# Returns: Forbidden
|
||||||
|
```
|
||||||
|
|
||||||
|
**Root Cause:** The API token `app_deployment...` has been revoked or has limited scope.
|
||||||
|
|
||||||
|
**Impact:**
|
||||||
|
- Cannot verify Docker image exists in registry
|
||||||
|
- Cannot test name availability (requires `environment.one`)
|
||||||
|
- Cannot create applications or compose stacks
|
||||||
|
- Cannot deploy portal to Dokploy
|
||||||
|
|
||||||
|
**Resolution Required:**
|
||||||
|
1. Log into Dokploy UI at https://app.flexinit.nl
|
||||||
|
2. Navigate to Settings → API Keys
|
||||||
|
3. Generate new API key with full permissions:
|
||||||
|
- Read/Write access to projects
|
||||||
|
- Read/Write access to applications
|
||||||
|
- Read/Write access to compose stacks
|
||||||
|
- Read/Write access to domains
|
||||||
|
4. Update `.env` with new token
|
||||||
|
5. Update BWS secret (ID: `6b3618fc-ba02-49bc-bdc8-b3c9004087bc`)
|
||||||
|
|
||||||
|
### Local Testing Results
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Health check - WORKS
|
||||||
|
curl -s "http://localhost:3000/health"
|
||||||
|
# {"status":"healthy","timestamp":"2026-01-13T13:01:46.100Z","version":"0.2.0",...}
|
||||||
|
|
||||||
|
# Name check - FAILS (API token issue)
|
||||||
|
curl -s "http://localhost:3000/api/check/test-stack"
|
||||||
|
# {"available":false,"valid":false,"error":"Failed to check availability"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Required .env Configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Added for shared project deployment
|
||||||
|
SHARED_PROJECT_ID=2y2Glhz5Wy0dBNf6BOR_-
|
||||||
|
SHARED_ENVIRONMENT_ID=RqE9OFMdLwkzN7pif1xN8
|
||||||
|
```
|
||||||
|
|
||||||
|
### Next Steps After Token Fix
|
||||||
|
|
||||||
|
1. Verify `project.all` API works with new token
|
||||||
|
2. Deploy portal to Dokploy (docker-compose.dev.yml)
|
||||||
|
3. Test end-to-end stack deployment
|
||||||
|
4. Verify stacks deploy to shared project
|
||||||
|
5. Clean up test deployments
|
||||||
|
|
||||||
|
### Commands Reference
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test API token
|
||||||
|
source .env && curl -s "https://app.flexinit.nl/api/project.all" \
|
||||||
|
-H "x-api-key: $DOKPLOY_API_TOKEN" | jq '.[].name'
|
||||||
|
|
||||||
|
# Get environment applications
|
||||||
|
source .env && curl -s "https://app.flexinit.nl/api/environment.one?environmentId=RqE9OFMdLwkzN7pif1xN8" \
|
||||||
|
-H "x-api-key: $DOKPLOY_API_TOKEN" | jq '.applications'
|
||||||
|
|
||||||
|
# Deploy test stack
|
||||||
|
curl -X POST http://localhost:3000/api/deploy \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"name":"test-'$(date +%s | tail -c 4)'"}'
|
||||||
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user