Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 72 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,78 @@ finmind/
```

## Deployment
- Backend: Dockerized Flask to Railway/Render free tier (Postgres & Redis managed or via Compose locally).
- Frontend: Vercel.
- Secrets: use environment variables (.env locally, platform secrets in cloud).
- Kubernetes manifests for full stack deployment are available in `deploy/k8s/`.

FinMind supports deployment to 14+ platforms. All configurations live in `deploy/`.

### Quick Start (Docker Compose — Recommended for Local Dev)
```bash
cp .env.example .env # edit secrets
docker compose up --build
# Frontend: http://localhost:5173 | Backend: http://localhost:8000
```

### Kubernetes (Helm Chart)
Full-stack Helm chart with HPA autoscaling, TLS-ready ingress, health probes, and Prometheus annotations.
```bash
# Install
helm install finmind deploy/helm/finmind/ \
--namespace finmind --create-namespace \
--set secrets.postgresPassword=<password> \
--set secrets.jwtSecret=<secret> \
--set ingress.enabled=true \
--set ingress.hosts[0].host=finmind.example.com

# Upgrade
helm upgrade finmind deploy/helm/finmind/ --namespace finmind

# Uninstall
helm uninstall finmind --namespace finmind
```

### Kubernetes (Tilt — Local K8s Dev)
```bash
# Prerequisites: Tilt, kind/minikube/Docker Desktop K8s
tilt up
# Opens Tilt dashboard with live-reload for backend and frontend
```

### Platform-as-a-Service (One-Click Deploys)

| Platform | Guide | Free Tier |
|----------|-------|-----------|
| **Railway** | [deploy/paas/railway/](deploy/paas/railway/) | ✅ |
| **Heroku** | [deploy/paas/heroku/](deploy/paas/heroku/) | ✅ |
| **DigitalOcean** | [deploy/paas/digitalocean/](deploy/paas/digitalocean/) | — |
| **Render** | [deploy/paas/render/](deploy/paas/render/) | ✅ |
| **Fly.io** | [deploy/paas/flyio/](deploy/paas/flyio/) | ✅ |
| **Netlify** | [deploy/paas/netlify/](deploy/paas/netlify/) (frontend) | ✅ |
| **Vercel** | [deploy/paas/vercel/](deploy/paas/vercel/) (frontend) | ✅ |

### Cloud Providers

| Provider | Guide |
|----------|-------|
| **AWS ECS Fargate** | [deploy/cloud/aws-ecs/](deploy/cloud/aws-ecs/) — CloudFormation template |
| **GCP Cloud Run** | [deploy/cloud/gcp-cloudrun/](deploy/cloud/gcp-cloudrun/) — deploy script |
| **Azure Container Apps** | [deploy/cloud/azure-container-apps/](deploy/cloud/azure-container-apps/) — deploy script |

### Environment Variables

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `DATABASE_URL` | ✅ | `postgresql+psycopg2://finmind:finmind@postgres:5432/finmind` | PostgreSQL connection |
| `REDIS_URL` | ✅ | `redis://redis:6379/0` | Redis connection |
| `JWT_SECRET` | ✅ | `dev-secret-change` | JWT signing key |
| `POSTGRES_USER` | ✅ | `finmind` | PostgreSQL username |
| `POSTGRES_PASSWORD` | ✅ | `finmind` | PostgreSQL password |
| `POSTGRES_DB` | ✅ | `finmind` | PostgreSQL database name |
| `VITE_API_URL` | Frontend | `http://localhost:8000` | Backend API URL |
| `LOG_LEVEL` | — | `INFO` | Logging level |
| `GEMINI_API_KEY` | — | — | Google Gemini API key |
| `OPENAI_API_KEY` | — | — | OpenAI API key |
| `GEMINI_MODEL` | — | `gemini-1.5-flash` | Gemini model name |

See each platform's README for platform-specific setup instructions.

## Local Development
1) Prereqs: Docker, Docker Compose, Node 20+, Python 3.11+
Expand Down
124 changes: 124 additions & 0 deletions Tiltfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# -*- mode: python -*-
# FinMind Tiltfile — local Kubernetes development workflow
# Prerequisites: Tilt (https://tilt.dev), a local K8s cluster (kind/minikube/Docker Desktop)

# ─── Configuration ───────────────────────────────────────────────────────────

# Allow deploying to the current kubectl context (restrict as needed)
allow_k8s_contexts(k8s_context())

# Load .env for local overrides
load('ext://dotenv', 'dotenv')
dotenv()

# ─── Container Images ────────────────────────────────────────────────────────

# Backend — live-reload via Tilt's live_update
docker_build(
'finmind-backend',
context='./packages/backend',
dockerfile='./packages/backend/Dockerfile',
live_update=[
sync('./packages/backend/app', '/app/app'),
sync('./packages/backend/wsgi.py', '/app/wsgi.py'),
run('pip install -r requirements.txt', trigger='./packages/backend/requirements.txt'),
],
)

# Frontend — live-reload
docker_build(
'finmind-frontend',
context='./app',
dockerfile='./app/Dockerfile',
live_update=[
sync('./app/src', '/app/src'),
sync('./app/public', '/app/public'),
sync('./app/index.html', '/app/index.html'),
run('npm install', trigger='./app/package.json'),
],
)

# ─── Kubernetes Resources ────────────────────────────────────────────────────

# Apply namespace first
k8s_yaml('deploy/k8s/namespace.yaml')

# Create dev secrets (uses defaults from .env.example if .env is absent)
local_resource(
'create-secrets',
cmd='''
kubectl create namespace finmind --dry-run=client -o yaml | kubectl apply -f - && \
kubectl create secret generic finmind-secrets \
--namespace=finmind \
--from-literal=POSTGRES_USER="${POSTGRES_USER:-finmind}" \
--from-literal=POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-finmind}" \
--from-literal=POSTGRES_DB="${POSTGRES_DB:-finmind}" \
--from-literal=JWT_SECRET="${JWT_SECRET:-dev-secret-change}" \
--from-literal=GEMINI_API_KEY="${GEMINI_API_KEY:-}" \
--dry-run=client -o yaml | kubectl apply -f -
''',
deps=['.env'],
)

# Deploy the full application stack
k8s_yaml('deploy/k8s/app-stack.yaml')

# ─── Resource Configuration ──────────────────────────────────────────────────

# Backend
k8s_resource(
'backend',
port_forwards=['8000:8000'],
resource_deps=['create-secrets', 'postgres', 'redis'],
labels=['app'],
)

# Frontend (using dev server via docker-compose, or the nginx build for k8s)
# For local dev, we port-forward the frontend service
k8s_resource(
'nginx',
port_forwards=['8080:80'],
resource_deps=['backend'],
labels=['app'],
new_name='frontend-proxy',
)

# Data stores
k8s_resource(
'postgres',
port_forwards=['5432:5432'],
resource_deps=['create-secrets'],
labels=['data'],
)

k8s_resource(
'redis',
port_forwards=['6379:6379'],
labels=['data'],
)

# ─── Monitoring (optional — uncomment to deploy) ─────────────────────────────

# k8s_yaml('deploy/k8s/monitoring-stack.yaml')
# k8s_resource('prometheus', port_forwards=['9090:9090'], labels=['monitoring'])
# k8s_resource('grafana', port_forwards=['3000:3000'], labels=['monitoring'])

# ─── Custom Buttons ──────────────────────────────────────────────────────────

# Run backend tests
local_resource(
'backend-tests',
cmd='docker compose exec backend pytest tests/ -v',
auto_init=False,
trigger_mode=TRIGGER_MODE_MANUAL,
labels=['test'],
)

# Init database manually
local_resource(
'init-db',
cmd='kubectl exec -n finmind deploy/backend -- python -m flask --app wsgi:app init-db',
auto_init=False,
trigger_mode=TRIGGER_MODE_MANUAL,
labels=['ops'],
)
89 changes: 89 additions & 0 deletions deploy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# FinMind — Deployment Configurations

This directory contains deployment configurations for every major platform.

```
deploy/
├── helm/ # Kubernetes Helm chart (full stack)
│ └── finmind/
│ ├── Chart.yaml
│ ├── values.yaml
│ └── templates/
│ ├── _helpers.tpl
│ ├── secrets.yaml
│ ├── configmap.yaml
│ ├── serviceaccount.yaml
│ ├── backend-deployment.yaml
│ ├── backend-service.yaml
│ ├── backend-hpa.yaml
│ ├── frontend-deployment.yaml
│ ├── frontend-service.yaml
│ ├── frontend-hpa.yaml
│ ├── postgres-deployment.yaml
│ ├── postgres-service.yaml
│ ├── postgres-pvc.yaml
│ ├── redis-deployment.yaml
│ ├── redis-service.yaml
│ ├── ingress.yaml
│ ├── servicemonitor.yaml
│ └── NOTES.txt
├── k8s/ # Raw Kubernetes manifests (existing)
│ ├── namespace.yaml
│ ├── app-stack.yaml
│ ├── monitoring-stack.yaml
│ └── secrets.example.yaml
├── paas/ # Platform-as-a-Service configs
│ ├── railway/ # Railway
│ │ ├── railway.toml
│ │ └── README.md
│ ├── heroku/ # Heroku
│ │ ├── heroku.yml
│ │ ├── app.json
│ │ └── README.md
│ ├── digitalocean/ # DigitalOcean App Platform
│ │ ├── do-app.yaml
│ │ └── README.md
│ ├── render/ # Render
│ │ ├── render.yaml
│ │ └── README.md
│ ├── flyio/ # Fly.io
│ │ ├── fly.toml
│ │ └── README.md
│ ├── netlify/ # Netlify (frontend only)
│ │ ├── netlify.toml
│ │ └── README.md
│ └── vercel/ # Vercel (frontend only)
│ ├── vercel.json
│ └── README.md
└── cloud/ # Cloud provider configs
├── aws-ecs/ # AWS ECS Fargate
│ ├── cloudformation.json
│ └── README.md
├── gcp-cloudrun/ # GCP Cloud Run
│ ├── deploy-cloudrun.sh
│ └── README.md
└── azure-container-apps/ # Azure Container Apps
├── deploy-azure.sh
└── README.md
```

## Quick Reference

| Platform | Type | Config File | Free Tier |
|----------|------|-------------|-----------|
| **Docker Compose** | Local | `docker-compose.yml` | ✅ |
| **Kubernetes (Helm)** | K8s | `deploy/helm/finmind/` | — |
| **Kubernetes (raw)** | K8s | `deploy/k8s/` | — |
| **Tilt** | Local K8s | `Tiltfile` | ✅ |
| **Railway** | PaaS | `deploy/paas/railway/` | ✅ |
| **Heroku** | PaaS | `deploy/paas/heroku/` | ✅ |
| **DigitalOcean** | PaaS | `deploy/paas/digitalocean/` | — |
| **Render** | PaaS | `deploy/paas/render/` | ✅ |
| **Fly.io** | PaaS | `deploy/paas/flyio/` | ✅ |
| **Netlify** | Static | `deploy/paas/netlify/` | ✅ |
| **Vercel** | Static | `deploy/paas/vercel/` | ✅ |
| **AWS ECS Fargate** | Cloud | `deploy/cloud/aws-ecs/` | — |
| **GCP Cloud Run** | Cloud | `deploy/cloud/gcp-cloudrun/` | ✅ |
| **Azure Container Apps** | Cloud | `deploy/cloud/azure-container-apps/` | — |

See each subdirectory's `README.md` for platform-specific instructions.
52 changes: 52 additions & 0 deletions deploy/cloud/aws-ecs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# AWS ECS Fargate Deployment Guide
#
# This CloudFormation template deploys FinMind on ECS Fargate with:
# - Application Load Balancer (path-based routing)
# - Backend and Frontend as separate ECS services
# - Auto-scaling (CPU-based, 2-10 tasks)
# - CloudWatch Logs
# - Container Insights
#
# Prerequisites:
# - AWS CLI configured with appropriate permissions
# - A VPC with at least 2 public subnets
# - Container images pushed to ECR or GHCR
# - SSM Parameter Store values created:
# /finmind/DATABASE_URL
# /finmind/REDIS_URL
# /finmind/JWT_SECRET
#
# Quick Start:
#
# 1. Store secrets in SSM Parameter Store:
# aws ssm put-parameter --name /finmind/DATABASE_URL --type SecureString \
# --value "postgresql+psycopg2://user:pass@host:5432/finmind"
# aws ssm put-parameter --name /finmind/REDIS_URL --type SecureString \
# --value "redis://host:6379/0"
# aws ssm put-parameter --name /finmind/JWT_SECRET --type SecureString \
# --value "$(openssl rand -hex 32)"
#
# 2. Deploy the stack:
# aws cloudformation deploy \
# --template-file deploy/cloud/aws-ecs/cloudformation.json \
# --stack-name finmind \
# --capabilities CAPABILITY_IAM \
# --parameter-overrides \
# VpcId=vpc-xxx \
# SubnetIds=subnet-aaa,subnet-bbb \
# DbPassword=your-secure-password
#
# 3. Get the ALB URL:
# aws cloudformation describe-stacks --stack-name finmind \
# --query 'Stacks[0].Outputs[?OutputKey==`ALBDnsName`].OutputValue' \
# --output text
#
# Database Options:
# - Amazon RDS PostgreSQL (recommended for production)
# - Amazon ElastiCache for Redis
# - Create these separately and pass connection strings via SSM
#
# Cost Optimization:
# - Use Fargate Spot for non-critical workloads
# - Scale down to 1 task during off-hours
# - Use free-tier eligible RDS and ElastiCache instances
Loading
Loading