feat: production-ready deployment with multi-language UI
- Add multi-language support (NL, AR, EN) with RTL
- Improve health checks (SSL-tolerant, multi-endpoint)
- Add DELETE /api/stack/:name for cleanup
- Add persistent storage (portal-ai-workspace-{name})
- Improve rollback (delete domain, app, project)
- Increase SSE timeout to 255s
- Add deployment strategy documentation
This commit is contained in:
290
docs/DEPLOYMENT_STRATEGY.md
Normal file
290
docs/DEPLOYMENT_STRATEGY.md
Normal file
@@ -0,0 +1,290 @@
|
||||
# AI Stack Deployer - Deployment Strategy
|
||||
|
||||
## Overview
|
||||
|
||||
Deploy the AI Stack Deployer portal to production, making it accessible at `deploy.ai.flexinit.nl`.
|
||||
|
||||
## User Stack Container (oh-my-opencode-free)
|
||||
|
||||
### Image Details
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Registry | `git.app.flexinit.nl` |
|
||||
| Image | `oussamadouhou/oh-my-opencode-free:latest` |
|
||||
| Base | `oven/bun:debian` |
|
||||
| Port | 8080 (OpenCode server) |
|
||||
| CI/CD | Gitea Actions (auto-build on push) |
|
||||
|
||||
### Pre-configured Free Models
|
||||
|
||||
| Agent | Model | Purpose |
|
||||
|-------|-------|---------|
|
||||
| Sisyphus (main) | `glm-4.7-free` | Primary coding |
|
||||
| Oracle | `gpt-5-nano` | Architecture |
|
||||
| Librarian | `minimax-m2.1-free` | Documentation |
|
||||
| Explore | `grok-code` | Codebase search |
|
||||
| Frontend | `glm-4.7-free` | UI/UX |
|
||||
| Document Writer | `gpt-5-nano` | Docs |
|
||||
|
||||
### Baked Configuration
|
||||
|
||||
```
|
||||
/shared/config/
|
||||
├── opencode.json # CLI config
|
||||
├── opencode.jsonc # Permissions
|
||||
└── oh-my-opencode.json # Agent model assignments
|
||||
```
|
||||
|
||||
### Persistent Storage (per user)
|
||||
|
||||
| Volume | Mount Path | Purpose |
|
||||
|--------|------------|---------|
|
||||
| `workspace-{name}` | `/workspace` | User projects |
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Internet
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Traefik (144.76.116.169) │
|
||||
│ - SSL termination (wildcard *.ai.flexinit.nl)│
|
||||
│ - Routes deploy.ai.flexinit.nl → portal │
|
||||
└─────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ AI Stack Deployer Container │
|
||||
│ - Port 3000 │
|
||||
│ - Needs access to 10.100.0.20 (Dokploy) │
|
||||
└─────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Dokploy (10.100.0.20:3000) │
|
||||
│ - Creates user stacks │
|
||||
│ - Manages containers │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Deployment Options
|
||||
|
||||
### Option A: Deploy via Dokploy (Recommended)
|
||||
|
||||
**Pros**: Self-service, consistent with stack deployments, automatic restarts
|
||||
**Cons**: Circular dependency (if Dokploy is down, can't redeploy)
|
||||
|
||||
### Option B: Direct Docker on Traefik Host
|
||||
|
||||
**Pros**: Independent of Dokploy, simpler network (same host as Traefik)
|
||||
**Cons**: Manual management, no Dokploy UI
|
||||
|
||||
### Option C: Separate VM with Docker Compose
|
||||
|
||||
**Pros**: Isolation, can use docker-compose.yml directly
|
||||
**Cons**: Extra VM, network routing complexity
|
||||
|
||||
**Decision: Option A (Dokploy)** - Eat our own dog food, leverage existing infrastructure.
|
||||
|
||||
## Step-by-Step Deployment
|
||||
|
||||
### Phase 1: Prepare Image
|
||||
|
||||
```bash
|
||||
# 1. Build and tag image
|
||||
docker build -t git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:latest .
|
||||
|
||||
# 2. Push to registry
|
||||
docker push git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:latest
|
||||
```
|
||||
|
||||
### Phase 2: Create Dokploy Project
|
||||
|
||||
1. Open Dokploy UI: https://app.flexinit.nl
|
||||
2. Create project: `ai-stack-deployer-portal`
|
||||
3. Create application:
|
||||
- Name: `deployer`
|
||||
- Type: Docker Image
|
||||
- Image: `git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:latest`
|
||||
|
||||
### Phase 3: Configure Environment
|
||||
|
||||
Set these environment variables in Dokploy:
|
||||
|
||||
| Variable | Value | Source |
|
||||
|----------|-------|--------|
|
||||
| `NODE_ENV` | `production` | Static |
|
||||
| `PORT` | `3000` | Static |
|
||||
| `HOST` | `0.0.0.0` | Static |
|
||||
| `DOKPLOY_URL` | `http://10.100.0.20:3000` | Static |
|
||||
| `DOKPLOY_API_TOKEN` | `<token>` | BWS: `6b3618fc-ba02-49bc-bdc8-b3c9004087bc` |
|
||||
| `STACK_DOMAIN_SUFFIX` | `ai.flexinit.nl` | Static |
|
||||
| `STACK_IMAGE` | `git.app.flexinit.nl/oussamadouhou/oh-my-opencode-free:latest` | Static |
|
||||
| `RESERVED_NAMES` | `admin,api,www,root,system,test,demo,portal,deploy` | Static |
|
||||
|
||||
### Phase 4: Configure Domain
|
||||
|
||||
1. In Dokploy application settings, add domain:
|
||||
- Host: `deploy.ai.flexinit.nl`
|
||||
- HTTPS: Enabled
|
||||
- Port: 3000
|
||||
|
||||
2. DNS is already configured (wildcard `*.ai.flexinit.nl` → 144.76.116.169)
|
||||
|
||||
### Phase 5: Deploy
|
||||
|
||||
1. Click "Deploy" in Dokploy UI
|
||||
2. Wait for container to start (~30s)
|
||||
3. Verify health: `curl https://deploy.ai.flexinit.nl/health`
|
||||
|
||||
## Network Requirements
|
||||
|
||||
The deployer container MUST be able to reach:
|
||||
- `10.100.0.20:3000` - Dokploy API (internal network)
|
||||
|
||||
This works automatically when deployed via Dokploy since containers share the internal network.
|
||||
|
||||
## Secrets Management
|
||||
|
||||
### Using BWS
|
||||
|
||||
```bash
|
||||
# Get Dokploy token
|
||||
bws secret get 6b3618fc-ba02-49bc-bdc8-b3c9004087bc
|
||||
|
||||
# Set in Dokploy environment variables (manually or via API)
|
||||
```
|
||||
|
||||
### Rotation Strategy
|
||||
|
||||
1. Generate new token in Dokploy UI
|
||||
2. Store in BWS
|
||||
3. Update environment variable in Dokploy
|
||||
4. Redeploy application
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Health Checks
|
||||
|
||||
- **Endpoint**: `GET /health`
|
||||
- **Interval**: 30 seconds
|
||||
- **Expected**: `{"status":"healthy",...}`
|
||||
|
||||
### Alerting
|
||||
|
||||
Set up monitoring for:
|
||||
- Health endpoint failures
|
||||
- High error rates in deployment logs
|
||||
- Circuit breaker state changes
|
||||
|
||||
## Rollback Procedure
|
||||
|
||||
### Quick Rollback
|
||||
|
||||
```bash
|
||||
# Via Dokploy UI
|
||||
1. Go to application → Deployments
|
||||
2. Click "Rollback" on previous successful deployment
|
||||
```
|
||||
|
||||
### Manual Rollback
|
||||
|
||||
```bash
|
||||
# Re-deploy previous image tag
|
||||
docker pull git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:v0.1.0
|
||||
# Update image in Dokploy and redeploy
|
||||
```
|
||||
|
||||
## CI/CD Pipeline (Future)
|
||||
|
||||
```yaml
|
||||
# .github/workflows/deploy.yml
|
||||
name: Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build and push
|
||||
run: |
|
||||
docker build -t git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:${{ github.sha }} .
|
||||
docker push git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:${{ github.sha }}
|
||||
|
||||
- name: Deploy via Dokploy API
|
||||
run: |
|
||||
curl -X POST "$DOKPLOY_URL/api/application.update" \
|
||||
-H "x-api-key: $DOKPLOY_API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"applicationId": "$APP_ID", "dockerImage": "...:${{ github.sha }}"}'
|
||||
|
||||
curl -X POST "$DOKPLOY_URL/api/application.deploy" \
|
||||
-H "x-api-key: $DOKPLOY_API_TOKEN" \
|
||||
-d '{"applicationId": "$APP_ID"}'
|
||||
```
|
||||
|
||||
## Pre-Deployment Checklist
|
||||
|
||||
- [ ] Docker image builds successfully: `docker build -t test .`
|
||||
- [ ] TypeScript compiles: `bun run typecheck`
|
||||
- [ ] Health endpoint works locally: `curl localhost:3000/health`
|
||||
- [ ] Dokploy API token is valid
|
||||
- [ ] Registry credentials configured
|
||||
- [ ] Domain DNS resolves correctly
|
||||
|
||||
## Post-Deployment Verification
|
||||
|
||||
```bash
|
||||
# 1. Health check
|
||||
curl https://deploy.ai.flexinit.nl/health
|
||||
|
||||
# 2. Name validation
|
||||
curl https://deploy.ai.flexinit.nl/api/check/test-name
|
||||
|
||||
# 3. Frontend loads
|
||||
curl -s https://deploy.ai.flexinit.nl | grep -q "AI Stack Deployer"
|
||||
|
||||
# 4. Full deployment test (optional)
|
||||
curl -X POST https://deploy.ai.flexinit.nl/api/deploy \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name": "smoke-test"}'
|
||||
|
||||
# 5. Cleanup test
|
||||
curl -X DELETE https://deploy.ai.flexinit.nl/api/stack/smoke-test
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **No public auth yet** - Consider adding authentication for production
|
||||
2. **Rate limiting** - Not implemented, consider adding
|
||||
3. **API token exposure** - Stored in Dokploy env vars (encrypted at rest)
|
||||
4. **Delete endpoint** - Should require authentication in production
|
||||
|
||||
## Estimated Timeline
|
||||
|
||||
| Phase | Duration | Notes |
|
||||
|-------|----------|-------|
|
||||
| Image build & push | 5 min | Automated |
|
||||
| Dokploy project setup | 10 min | One-time |
|
||||
| Environment config | 5 min | One-time |
|
||||
| Deployment | 2 min | Per deploy |
|
||||
| Verification | 5 min | Per deploy |
|
||||
| **Total first deploy** | ~30 min | |
|
||||
| **Subsequent deploys** | ~10 min | |
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Build and push Docker image
|
||||
2. Create Dokploy project and application
|
||||
3. Configure environment variables
|
||||
4. Add domain
|
||||
5. Deploy and verify
|
||||
6. Set up monitoring
|
||||
7. (Optional) Implement CI/CD
|
||||
Reference in New Issue
Block a user