Recommended for: Production deployments with 50+ users
Prerequisites
Teable Images
- Application image:
ghcr.io/teableio/teable:latest
Runtime Environment
- Running Kubernetes cluster
Required External Services
Database Service
- PostgreSQL database: postgres:15.4 (version > 12)
Cache Service
- Redis cache service: redis:7.2.4 (version > 5)
Object Storage Service
- MinIO object storage service:
minio/minio:RELEASE.2025-04-22T22-12-26Z
- kubectl command-line tool
Dependency Component Configuration
File Storage (MinIO)
File storage service must be accessible from the public internet (directly accessible by end users)
Two buckets need to be created in advance for file storage:
| Bucket | Environment Variable | Permission |
|---|
| Public bucket | BACKEND_STORAGE_PUBLIC_BUCKET=teable-pub | Public read (anonymous access required) |
| Private bucket | BACKEND_STORAGE_PRIVATE_BUCKET=teable-pvt | Private (default) |
The public bucket must have anonymous read access enabled. Without this, users cannot view shared images, avatars, and other public files.
Create Buckets Using MinIO Client (mc)
Before deploying Teable, create the required buckets:
# Set up MinIO client alias
mc alias set teable-minio https://minio.example.com YOUR_ACCESS_KEY YOUR_SECRET_KEY
# Create public bucket and set anonymous read access
mc mb --ignore-existing teable-minio/teable-pub
mc anonymous set public teable-minio/teable-pub
# Create private bucket
mc mb --ignore-existing teable-minio/teable-pvt
# Verify buckets exist
mc ls teable-minio
# Expected output:
# [2024-01-01 00:00:00 UTC] 0B teable-pub/
# [2024-01-01 00:00:00 UTC] 0B teable-pvt/
Teable MinIO Environment Variables Overview
# Fixed value
BACKEND_STORAGE_PROVIDER=minio
# Public bucket
BACKEND_STORAGE_PUBLIC_BUCKET=teable-pub
# Private bucket
BACKEND_STORAGE_PRIVATE_BUCKET=teable-pvt
# Public endpoint, important! Must be accessible by end users
BACKEND_STORAGE_MINIO_ENDPOINT=minio.example.com
# Same as above but with protocol
STORAGE_PREFIX=https://minio.example.com
# Internal network endpoint (Kubernetes DNS format: <service>.<namespace>.svc.cluster.local)
# ⚠️ Replace <namespace> with your actual namespace, e.g., minio.teable.svc.cluster.local
BACKEND_STORAGE_MINIO_INTERNAL_ENDPOINT=minio.teable.svc.cluster.local
# Public port, typically 443 or 9000
BACKEND_STORAGE_MINIO_PORT=443
# Internal network port, typically 80 or 9000
BACKEND_STORAGE_MINIO_INTERNAL_PORT=80
# Enable HTTPS, note: if Teable uses HTTPS, MinIO must also use HTTPS to avoid CORS issues
BACKEND_STORAGE_MINIO_USE_SSL="true"
# Admin account
BACKEND_STORAGE_MINIO_ACCESS_KEY=root
# Admin password
BACKEND_STORAGE_MINIO_SECRET_KEY=rootPassword
Database
Create a database account with administrative privileges, a database, and set a password.
Environment variables example:
- Database name: teable
- Password: your-password
- Username: postgres
- Port: 5432
PRISMA_DATABASE_URL="postgresql://postgres:your-password@your-postgres-host:5432/teable"
Redis Cache
Teable only needs the internal network address for Redis cache configuration. (Note: Redis manages both cache and queues, it’s essential. Data should be backed up regularly)
Environment variables example:
BACKEND_CACHE_REDIS_URI="redis://username:password@your-redis-host:6379/0"
Create Configuration Files
teable-config.yaml (Non-sensitive configuration)
apiVersion: v1
kind: ConfigMap
metadata:
name: teable-config
namespace: teable # Replace with your namespace
data:
# Application base configuration, public access domain
PUBLIC_ORIGIN: "https://your-domain.com"
# Storage configuration
BACKEND_STORAGE_PROVIDER: "minio"
# Bucket names (must be created in MinIO before deployment)
BACKEND_STORAGE_PUBLIC_BUCKET: "teable-pub"
BACKEND_STORAGE_PRIVATE_BUCKET: "teable-pvt"
# Public endpoint, important! Must be accessible by end users
BACKEND_STORAGE_MINIO_ENDPOINT: "minio.example.com"
# Same as above but with protocol
STORAGE_PREFIX: "https://minio.example.com"
# Internal endpoint (Kubernetes DNS format: <service>.<namespace>.svc.cluster.local)
# ⚠️ Replace 'teable' with your actual namespace
BACKEND_STORAGE_MINIO_INTERNAL_ENDPOINT: "minio.teable.svc.cluster.local"
# Public port, typically 443 or 9000
BACKEND_STORAGE_MINIO_PORT: "443"
# Internal port, typically 80 or 9000
BACKEND_STORAGE_MINIO_INTERNAL_PORT: "80"
# Enable HTTPS, note: if Teable uses HTTPS, MinIO must also use HTTPS to avoid CORS issues
BACKEND_STORAGE_MINIO_USE_SSL: "true"
# Cache configuration, fixed value
BACKEND_CACHE_PROVIDER: "redis"
# Other configurations, fixed values
NEXT_ENV_IMAGES_ALL_REMOTE: "true"
PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING: "1"
# Keep this when using self-signed certificates
NODE_TLS_REJECT_UNAUTHORIZED: '0'
teable-secrets.yaml (Sensitive information)
apiVersion: v1
kind: Secret
metadata:
name: teable-secrets
namespace: teable # Replace with your namespace
type: Opaque
stringData:
# Database sensitive information
PRISMA_DATABASE_URL: "postgresql://postgres:your-password@your-postgres-host:5432/teable"
# Application secrets
BACKEND_JWT_SECRET: "your-jwt-secret"
BACKEND_SESSION_SECRET: "your-session-secret"
# MinIO authentication
BACKEND_STORAGE_MINIO_ACCESS_KEY: "your-minio-access-key"
BACKEND_STORAGE_MINIO_SECRET_KEY: "your-minio-secret-key"
# Redis authentication
BACKEND_CACHE_REDIS_URI: "redis://username:password@your-redis-host:6379/0"
apiVersion: apps/v1
kind: Deployment
metadata:
name: teable
namespace: teable # Replace with your namespace
spec:
replicas: 1 # Configure as needed
selector:
matchLabels:
app: teable
template:
metadata:
labels:
app: teable
spec:
# Add initContainers for database migration
initContainers:
- name: db-migrate
image: ghcr.io/teableio/teable:latest
args:
- migrate-only
envFrom:
- configMapRef:
name: teable-config
- secretRef:
name: teable-secrets
resources:
requests:
cpu: 100m
memory: 102Mi
limits:
cpu: 1000m
memory: 1024Mi
containers:
- name: teable
image: ghcr.io/teableio/teable:latest
args:
- skip-migrate
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: teable-config
- secretRef:
name: teable-secrets
resources:
requests:
cpu: 200m
memory: 400Mi
limits:
cpu: 2000m
memory: 4096Mi
startupProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 30
successThreshold: 1
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 1
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 1
apiVersion: v1
kind: Service
metadata:
name: teable
namespace: teable # Replace with your namespace
spec:
ports:
- port: 3000
targetPort: 3000
selector:
app: teable
teable-ingress.yaml (Optional)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: teable
namespace: teable # Replace with your namespace
# Example using nginx, if using other ingress class, please replace
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
nginx.ingress.kubernetes.io/proxy-buffer-size: "128k"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-request-buffering: "off"
spec:
ingressClassName: nginx # Change to your ingress class if different
rules:
- host: your-domain.com # Replace with your domain
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: teable
port:
number: 3000
tls:
- hosts:
- your-domain.com # Replace with your domain
secretName: your-tls-secret
Do NOT add nginx.ingress.kubernetes.io/rewrite-target annotation. This will break Teable’s internal routing.
Deployment Steps
Before deploying Teable, ensure that:
- MinIO buckets (
teable-pub and teable-pvt) are created
- Public bucket has anonymous read access enabled
- PostgreSQL database
teable exists
- Redis is accessible
- Create namespace (if not exists):
kubectl create namespace teable
- Create configuration and secrets:
kubectl apply -f teable-config.yaml
kubectl apply -f teable-secrets.yaml
- Deploy application:
kubectl apply -f teable-deployment.yaml
kubectl apply -f teable-service.yaml
kubectl apply -f teable-ingress.yaml # Optional, if using Ingress
- Verify deployment:
# Check Pod status
kubectl get pods -n teable -l app=teable
# Wait for pods to be ready
kubectl rollout status deployment/teable -n teable
# View application logs
kubectl logs -n teable -l app=teable
Configuration Notes
- Sensitive Information Management
- All password, secret-related information should be managed through Secrets
- Database Configuration
- Ensure that the teable database exists in the database
- The database user needs to have appropriate permissions
- It’s recommended to use a connection pool to manage database connections
- MinIO Configuration
- Ensure that the storage bucket exists and the permissions are correct, one public bucket, one private bucket
- The public bucket needs to be completely publicly readable
- The internal and external access address configuration is correct
- Redis Configuration
- It’s recommended to enable Redis persistence
- Configure appropriate memory limits
- Consider using Redis cluster to improve availability
- Security Recommendations
- Use strong passwords and secrets
- Enable TLS/SSL encryption
- Regularly update certificates
- Limit network access scope
- Resource Configuration
- Adjust resource limits based on actual load
- Monitor resource usage
- Configure appropriate health check parameters
Troubleshooting
Common Issues
| Error | Cause | Solution |
|---|
ECONNREFUSED to MinIO | MinIO internal endpoint incorrect or unreachable | Verify BACKEND_STORAGE_MINIO_INTERNAL_ENDPOINT uses correct format: <service>.<namespace>.svc.cluster.local |
Bucket not found | MinIO buckets not created | Create buckets using mc mb command before deployment |
Access Denied on file upload | MinIO credentials incorrect | Verify BACKEND_STORAGE_MINIO_ACCESS_KEY and SECRET_KEY |
Page not found on web access | Ingress rewrite-target configured | Remove rewrite-target annotation from Ingress |
Pod stuck in CrashLoopBackOff | Backend failed to start | Check logs for MinIO, database, or Redis connectivity issues |
Diagnostic Commands
- Check Pod status:
kubectl describe pod -n teable -l app=teable
- View application logs:
kubectl logs -n teable -l app=teable
kubectl logs -n teable -l app=teable --previous # If pod crashed
- Verify configuration:
kubectl describe configmap teable-config -n teable
kubectl describe secret teable-secrets -n teable
- Check network connection:
kubectl exec -it <pod-name> -n teable -- curl -v localhost:3000/health
- Test MinIO connectivity from inside the cluster:
kubectl run test-minio --rm -it --image=curlimages/curl --restart=Never -n teable -- \
curl -v http://minio.teable.svc.cluster.local/minio/health/live