> ## Documentation Index
> Fetch the complete documentation index at: https://help.teable.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Kubernetes Deployment

<Callout type="info">
  **Recommended for:** Production deployments with 50+ users
</Callout>

<Tip>
  For resource sizing recommendations (CPU, memory, replicas), see [Self-Hosted Overview → Resource Recommendations](/en/deploy/production-overview#resource-recommendations).
</Tip>

## 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)

<Note>File storage service must be accessible from the public internet (directly accessible by end users)</Note>

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)                           |

<Warning>
  The public bucket **must** have anonymous read access enabled. Without this, users cannot view shared images, avatars, and other public files.
</Warning>

#### Create Buckets Using MinIO Client (mc)

Before deploying Teable, create the required buckets:

```sh theme={null}
# 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

```sh theme={null}
# 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

```sh theme={null}
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:

```sh theme={null}
BACKEND_CACHE_REDIS_URI="redis://username:password@your-redis-host:6379/0"
```

## Create Configuration Files

```yaml teable-config.yaml (Non-sensitive configuration) theme={null}
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'
```

```yaml teable-secrets.yaml (Sensitive information) theme={null}
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"
```

```yaml teable-deployment.yaml theme={null}
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
```

```yaml teable-service.yaml theme={null}
apiVersion: v1
kind: Service
metadata:
  name: teable
  namespace: teable  # Replace with your namespace
spec:
  ports:
    - port: 3000
      targetPort: 3000
  selector:
    app: teable
```

```yaml teable-ingress.yaml (Optional) theme={null}
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 
```

<Warning>
  Do **NOT** add `nginx.ingress.kubernetes.io/rewrite-target` annotation. This will break Teable's internal routing.
</Warning>

## Deployment Steps

<Warning>
  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
</Warning>

1. Create namespace (if not exists):

```sh theme={null}
kubectl create namespace teable
```

2. Create configuration and secrets:

```sh theme={null}
kubectl apply -f teable-config.yaml
kubectl apply -f teable-secrets.yaml
```

3. Deploy application:

```sh theme={null}
kubectl apply -f teable-deployment.yaml
kubectl apply -f teable-service.yaml
kubectl apply -f teable-ingress.yaml  # Optional, if using Ingress
```

4. Verify deployment:

```sh theme={null}
# 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

2. 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

3. 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

4. Redis Configuration

* It's recommended to enable Redis persistence
* Configure appropriate memory limits
* Consider using Redis cluster to improve availability

5. Security Recommendations

* Use strong passwords and secrets
* Enable TLS/SSL encryption
* Regularly update certificates
* Limit network access scope

6. 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

1. Check Pod status:

```sh theme={null}
kubectl describe pod -n teable -l app=teable
```

2. View application logs:

```sh theme={null}
kubectl logs -n teable -l app=teable
kubectl logs -n teable -l app=teable --previous  # If pod crashed
```

3. Verify configuration:

```sh theme={null}
kubectl describe configmap teable-config -n teable
kubectl describe secret teable-secrets -n teable
```

4. Check network connection:

```sh theme={null}
kubectl exec -it <pod-name> -n teable -- curl -v localhost:3000/health
```

5. Test MinIO connectivity from inside the cluster:

```sh theme={null}
kubectl run test-minio --rm -it --image=curlimages/curl --restart=Never -n teable -- \
  curl -v http://minio.teable.svc.cluster.local/minio/health/live
```
