> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/temporalio/temporal/llms.txt
> Use this file to discover all available pages before exploring further.

# Docker Deployment

> Deploy Temporal Server using Docker containers

This guide covers deploying Temporal Server using Docker, from local development setups to production-ready containerized deployments.

## Quick Start

The fastest way to get Temporal running locally:

```bash theme={null}
brew install temporal
temporal server start-dev
```

This starts a server with in-memory SQLite storage suitable for development.

## Docker Image

Temporal provides official Docker images:

```bash theme={null}
docker pull temporalio/server:latest
```

### Image Details

The server image is based on Alpine Linux and includes:

* `temporal-server` binary in `/usr/local/bin/`
* Entrypoint script at `/etc/temporal/entrypoint.sh`
* Non-root user `temporal` (UID 1000, GID 1000)

## Docker Compose Setup

### Development Environment

For local development with all dependencies:

<Steps>
  <Step title="Create docker-compose.yml">
    ```yaml docker-compose.yml theme={null}
    services:
      mysql:
        image: mysql:8.0.29-oracle
        container_name: temporal-mysql
        ports:
          - "3306:3306"
        environment:
          MYSQL_ROOT_PASSWORD: root
        volumes:
          - ./mysql-init:/docker-entrypoint-initdb.d
        networks:
          - temporal-network
      
      elasticsearch:
        image: elasticsearch:7.10.1
        container_name: temporal-elasticsearch
        ports:
          - "9200:9200"
        environment:
          - discovery.type=single-node
          - ES_JAVA_OPTS=-Xms100m -Xmx100m
        networks:
          - temporal-network
      
      temporal:
        image: temporalio/server:latest
        container_name: temporal-server
        depends_on:
          - mysql
        ports:
          - "7233:7233"
          - "7243:7243"
          - "8000:8000"
        environment:
          - DB=mysql8
          - MYSQL_SEEDS=mysql
          - MYSQL_USER=temporal
          - MYSQL_PWD=temporal
          - ENABLE_ES=false
          - DYNAMIC_CONFIG_FILE_PATH=/etc/temporal/config/dynamicconfig/docker.yaml
        volumes:
          - ./config:/etc/temporal/config
        networks:
          - temporal-network
      
      temporal-ui:
        image: temporalio/ui:latest
        container_name: temporal-ui
        depends_on:
          - temporal
        ports:
          - "8233:8080"
        environment:
          - TEMPORAL_ADDRESS=temporal:7233
        networks:
          - temporal-network

    networks:
      temporal-network:
        driver: bridge
    ```
  </Step>

  <Step title="Create MySQL initialization script">
    ```sql mysql-init/init.sql theme={null}
    CREATE USER 'temporal'@'%' IDENTIFIED BY 'temporal';
    GRANT ALL PRIVILEGES ON *.* TO 'temporal'@'%';
    FLUSH PRIVILEGES;
    ```
  </Step>

  <Step title="Start services">
    ```bash theme={null}
    docker-compose up -d
    ```
  </Step>

  <Step title="Initialize database schema">
    ```bash theme={null}
    # Create databases
    docker exec temporal-server temporal-sql-tool \
      --ep mysql --port 3306 \
      --user temporal --password temporal \
      --db temporal --plugin mysql8 create

    docker exec temporal-server temporal-sql-tool \
      --ep mysql --port 3306 \
      --user temporal --password temporal \
      --db temporal_visibility --plugin mysql8 create

    # Setup schema
    docker exec temporal-server temporal-sql-tool \
      --ep mysql --port 3306 \
      --user temporal --password temporal \
      --db temporal --plugin mysql8 \
      setup-schema -v 0.0

    docker exec temporal-server temporal-sql-tool \
      --ep mysql --port 3306 \
      --user temporal --password temporal \
      --db temporal --plugin mysql8 \
      update-schema --schema-name mysql/v8/temporal

    # Repeat for visibility
    docker exec temporal-server temporal-sql-tool \
      --ep mysql --port 3306 \
      --user temporal --password temporal \
      --db temporal_visibility --plugin mysql8 \
      setup-schema -v 0.0

    docker exec temporal-server temporal-sql-tool \
      --ep mysql --port 3306 \
      --user temporal --password temporal \
      --db temporal_visibility --plugin mysql8 \
      update-schema --schema-name mysql/v8/visibility
    ```
  </Step>
</Steps>

### Access the UI

Open [http://localhost:8233](http://localhost:8233) to access Temporal Web UI.

## Environment Variables

The Docker image supports configuration via environment variables:

### Database Configuration

<Tabs>
  <Tab title="MySQL">
    ```bash theme={null}
    DB=mysql8
    MYSQL_SEEDS=mysql-host
    MYSQL_USER=temporal
    MYSQL_PWD=temporal
    DB_PORT=3306
    DBNAME=temporal
    VISIBILITY_DBNAME=temporal_visibility

    # Connection pool
    SQL_MAX_CONNS=20
    SQL_MAX_IDLE_CONNS=20
    SQL_MAX_CONN_TIME=1h

    # TLS (optional)
    SQL_TLS_ENABLED=false
    SQL_CA=/path/to/ca.pem
    SQL_CERT=/path/to/cert.pem
    SQL_CERT_KEY=/path/to/key.pem
    ```
  </Tab>

  <Tab title="PostgreSQL">
    ```bash theme={null}
    DB=postgres12
    POSTGRES_SEEDS=postgres-host
    POSTGRES_USER=temporal
    POSTGRES_PWD=temporal
    DB_PORT=5432
    DBNAME=temporal
    VISIBILITY_DBNAME=temporal_visibility

    # Connection pool
    SQL_MAX_CONNS=20
    SQL_MAX_IDLE_CONNS=20
    SQL_MAX_CONN_TIME=1h
    ```
  </Tab>

  <Tab title="Cassandra">
    ```bash theme={null}
    DB=cassandra
    CASSANDRA_SEEDS=cassandra-host
    CASSANDRA_PORT=9042
    KEYSPACE=temporal
    CASSANDRA_USER=cassandra
    CASSANDRA_PASSWORD=cassandra

    # Connection pool
    CASSANDRA_MAX_CONNS=20

    # TLS (optional)
    CASSANDRA_TLS_ENABLED=false
    CASSANDRA_CA=/path/to/ca.pem
    ```
  </Tab>
</Tabs>

### Elasticsearch Configuration

```bash theme={null}
ENABLE_ES=true
ES_VERSION=v7
ES_SCHEME=http
ES_SEEDS=elasticsearch-host
ES_PORT=9200
ES_USER=elastic
ES_PWD=changeme
ES_VIS_INDEX=temporal_visibility_v1_dev
```

### Service Configuration

```bash theme={null}
# History shards
NUM_HISTORY_SHARDS=4

# Service ports
FRONTEND_GRPC_PORT=7233
FRONTEND_HTTP_PORT=7243
FRONTEND_MEMBERSHIP_PORT=6933

HISTORY_GRPC_PORT=7234
HISTORY_MEMBERSHIP_PORT=6934

MATCHING_GRPC_PORT=7235
MATCHING_MEMBERSHIP_PORT=6935

WORKER_GRPC_PORT=7239
WORKER_MEMBERSHIP_PORT=6939

# Bind address
BIND_ON_IP=0.0.0.0
TEMPORAL_BROADCAST_ADDRESS=
```

### Logging and Metrics

```bash theme={null}
# Logging
LOG_LEVEL=info

# Prometheus metrics
PROMETHEUS_ENDPOINT=0.0.0.0:8000
PROMETHEUS_TIMER_TYPE=histogram

# Or StatsD metrics
STATSD_ENDPOINT=statsd-host:8125

# Profiling
PPROF_PORT=7936
```

### TLS Configuration

```bash theme={null}
# Internode TLS
TEMPORAL_TLS_REQUIRE_CLIENT_AUTH=true
TEMPORAL_TLS_SERVER_CERT=/certs/server-cert.pem
TEMPORAL_TLS_SERVER_KEY=/certs/server-key.pem
TEMPORAL_TLS_SERVER_CA_CERT=/certs/ca-cert.pem
TEMPORAL_TLS_INTERNODE_SERVER_NAME=temporal-server

# Frontend TLS
TEMPORAL_TLS_FRONTEND_CERT=/certs/frontend-cert.pem
TEMPORAL_TLS_FRONTEND_KEY=/certs/frontend-key.pem
TEMPORAL_TLS_CLIENT1_CA_CERT=/certs/client-ca-cert.pem
TEMPORAL_TLS_FRONTEND_SERVER_NAME=temporal-frontend
```

## Production Deployment

### Multi-Container Setup

For production, run separate containers for each service:

<CodeGroup>
  ```yaml docker-compose-production.yml theme={null}
  services:
    temporal-frontend:
      image: temporalio/server:latest
      command:
        - /usr/local/bin/temporal-server
        - start
        - --service
        - frontend
      environment:
        - SERVICES=frontend
      deploy:
        replicas: 2
        resources:
          limits:
            cpus: '2'
            memory: 4G
      networks:
        - temporal-network
    
    temporal-history:
      image: temporalio/server:latest
      command:
        - /usr/local/bin/temporal-server
        - start
        - --service
        - history
      environment:
        - SERVICES=history
        - NUM_HISTORY_SHARDS=512
      deploy:
        replicas: 3
        resources:
          limits:
            cpus: '4'
            memory: 8G
      networks:
        - temporal-network
    
    temporal-matching:
      image: temporalio/server:latest
      command:
        - /usr/local/bin/temporal-server
        - start
        - --service
        - matching
      environment:
        - SERVICES=matching
      deploy:
        replicas: 2
        resources:
          limits:
            cpus: '2'
            memory: 4G
      networks:
        - temporal-network
    
    temporal-worker:
      image: temporalio/server:latest
      command:
        - /usr/local/bin/temporal-server
        - start
        - --service
        - worker
      environment:
        - SERVICES=worker
      deploy:
        replicas: 1
        resources:
          limits:
            cpus: '2'
            memory: 4G
      networks:
        - temporal-network

  networks:
    temporal-network:
      driver: overlay
  ```
</CodeGroup>

### Health Checks

Add health checks to your containers:

```yaml theme={null}
healthcheck:
  test: ["CMD", "/usr/local/bin/temporal-server", "health"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 40s
```

### Volume Mounts

Mount configuration and TLS certificates:

```yaml theme={null}
volumes:
  - ./config/production.yaml:/etc/temporal/config/config.yaml:ro
  - ./certs:/etc/temporal/certs:ro
  - ./dynamicconfig:/etc/temporal/config/dynamicconfig:ro
```

## Dockerfile Reference

The official Temporal Server Dockerfile:

```dockerfile theme={null}
ARG ALPINE_TAG=3.23.3
FROM alpine:${ALPINE_TAG}

ARG TARGETARCH

RUN apk add --no-cache \
    ca-certificates \
    tzdata && addgroup -g 1000 temporal && \
    adduser -u 1000 -G temporal -D temporal

COPY --chmod=755 ./build/${TARGETARCH}/temporal-server /usr/local/bin/
COPY --chmod=755 ./scripts/sh/entrypoint.sh /etc/temporal/entrypoint.sh

WORKDIR /etc/temporal
USER temporal

CMD [ "/etc/temporal/entrypoint.sh" ]
```

### Entrypoint Script

```bash theme={null}
#!/bin/sh
set -eu -o pipefail

# Resolve hostname to IP address if not already set
: "${BIND_ON_IP:=$(getent hosts "$(hostname)" | awk '{print $1;}')}" 
export BIND_ON_IP

# Set broadcast address for wildcard binds
if [ "${BIND_ON_IP}" = "0.0.0.0" ] || [ "${BIND_ON_IP}" = "::0" ]; then
    : "${TEMPORAL_BROADCAST_ADDRESS:=$(getent hosts "$(hostname)" | awk '{print $1;}')}" 
    export TEMPORAL_BROADCAST_ADDRESS
fi

exec temporal-server start
```

## Common Issues

<AccordionGroup>
  <Accordion title="Database connection errors">
    **Symptoms**: Container fails to start with database connection errors

    **Solutions**:

    * Ensure database is running and accessible
    * Verify network connectivity between containers
    * Check database credentials in environment variables
    * Initialize schema using `temporal-sql-tool` or `temporal-cassandra-tool`
  </Accordion>

  <Accordion title="Port conflicts">
    **Symptoms**: "Address already in use" errors

    **Solutions**:

    * Check for other services using the same ports
    * Modify port mappings in docker-compose.yml
    * Use environment variables to change default ports
  </Accordion>

  <Accordion title="Insufficient resources">
    **Symptoms**: Container OOM killed or high CPU usage

    **Solutions**:

    * Increase Docker memory limits
    * Reduce `NUM_HISTORY_SHARDS` for development
    * Lower database connection pool sizes
    * Scale services horizontally
  </Accordion>
</AccordionGroup>

<Note>
  For Kubernetes deployments, see the official [Temporal Helm charts](https://github.com/temporalio/helm-charts).
</Note>
