Skip to main content
Recommended for: Production deployments with 50+ users
For resource sizing recommendations (CPU, memory, replicas), see Self-Hosted Overview → Resource Recommendations.

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)
    • PG image: postgres:15.4

Cache Service

  • Redis cache service: redis:7.2.4 (version > 5)
    • Redis image: redis:7.2.4

Object Storage Service

  • MinIO object storage service: minio/minio:RELEASE.2025-04-22T22-12-26Z

Required Tools

  • 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:
BucketEnvironment VariablePermission
Public bucketBACKEND_STORAGE_PUBLIC_BUCKET=teable-pubPublic read (anonymous access required)
Private bucketBACKEND_STORAGE_PRIVATE_BUCKET=teable-pvtPrivate (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"
teable-deployment.yaml
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
teable-service.yaml
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:
  1. MinIO buckets (teable-pub and teable-pvt) are created
  2. Public bucket has anonymous read access enabled
  3. PostgreSQL database teable exists
  4. Redis is accessible
  1. Create namespace (if not exists):
kubectl create namespace teable
  1. Create configuration and secrets:
kubectl apply -f teable-config.yaml
kubectl apply -f teable-secrets.yaml
  1. Deploy application:
kubectl apply -f teable-deployment.yaml
kubectl apply -f teable-service.yaml
kubectl apply -f teable-ingress.yaml  # Optional, if using Ingress
  1. 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

  1. Sensitive Information Management
  • All password, secret-related information should be managed through Secrets
  1. 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
  1. 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
  1. Redis Configuration
  • It’s recommended to enable Redis persistence
  • Configure appropriate memory limits
  • Consider using Redis cluster to improve availability
  1. Security Recommendations
  • Use strong passwords and secrets
  • Enable TLS/SSL encryption
  • Regularly update certificates
  • Limit network access scope
  1. Resource Configuration
  • Adjust resource limits based on actual load
  • Monitor resource usage
  • Configure appropriate health check parameters

Troubleshooting

Common Issues

ErrorCauseSolution
ECONNREFUSED to MinIOMinIO internal endpoint incorrect or unreachableVerify BACKEND_STORAGE_MINIO_INTERNAL_ENDPOINT uses correct format: <service>.<namespace>.svc.cluster.local
Bucket not foundMinIO buckets not createdCreate buckets using mc mb command before deployment
Access Denied on file uploadMinIO credentials incorrectVerify BACKEND_STORAGE_MINIO_ACCESS_KEY and SECRET_KEY
Page not found on web accessIngress rewrite-target configuredRemove rewrite-target annotation from Ingress
Pod stuck in CrashLoopBackOffBackend failed to startCheck logs for MinIO, database, or Redis connectivity issues

Diagnostic Commands

  1. Check Pod status:
kubectl describe pod -n teable -l app=teable
  1. View application logs:
kubectl logs -n teable -l app=teable
kubectl logs -n teable -l app=teable --previous  # If pod crashed
  1. Verify configuration:
kubectl describe configmap teable-config -n teable
kubectl describe secret teable-secrets -n teable
  1. Check network connection:
kubectl exec -it <pod-name> -n teable -- curl -v localhost:3000/health
  1. 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
Last modified on February 10, 2026