---
title: Docker Compose
description: "Deploy self-hosted Tracecat using Docker Compose: bring up the API, executor, worker, frontend, Postgres, and Temporal stack on a single host in minutes."
---

<Warning>
  Docker Compose deployments default to basic email / password authentication.
  Production deployments should use [OIDC](/authentication/oidc) or [SAML](/authentication/saml) SSO.
  See [Security](/self-hosting/security) for additional production hardening recommendations.
</Warning>

## Prerequisites

- [Docker](https://docs.docker.com/engine/install/) version 26.0.0+
- [Docker Compose](https://docs.docker.com/compose/install/) version 2.29.0+
- [openssl](https://www.openssl.org/source/)

## Core secrets

The `env.sh` script (next step) generates these automatically. If you need to generate them manually:

<Snippet file="generate-secrets.mdx" />

## Download configuration files

```bash
# 1. Download the env.sh installation script
curl -o env.sh https://raw.githubusercontent.com/TracecatHQ/tracecat/1.0.0-beta.48/env.sh

# 2. Download the .env.example template file (env.sh needs this to generate your .env file)
curl -o .env.example https://raw.githubusercontent.com/TracecatHQ/tracecat/1.0.0-beta.48/.env.example

# 3. Make the env.sh script executable and run it
chmod +x env.sh && ./env.sh
```

After running `env.sh`, you'll be prompted to input the following:

- Set `PUBLIC_APP_URL`. Defaults to `localhost`.
- Require PostgreSQL SSL mode? Defaults to `n`.
- Required field: Enter email address for the first user (superadmin).

## Download Caddyfile

Tracecat uses Caddy as a reverse proxy.
You'll need to download the following `Caddyfile` to configure this service.

```bash
curl -o Caddyfile https://raw.githubusercontent.com/TracecatHQ/tracecat/1.0.0-beta.48/Caddyfile
```

## Download Docker Compose file

```bash
curl -o docker-compose.yml https://raw.githubusercontent.com/TracecatHQ/tracecat/1.0.0-beta.48/docker-compose.yml
```

## Start Tracecat

Run the command below to start Tracecat and all related services.
Make sure your `docker-compose.yml` and generated `.env` files are in the same directory.

```bash
docker compose up
```

## Access Tracecat

Once deployed, access your instance at:
- UI: `http://localhost:${PUBLIC_APP_PORT}`
- API docs: `http://localhost:${PUBLIC_APP_PORT}/api/docs`
- MCP: `http://localhost:${PUBLIC_APP_PORT}/mcp`

## Updating Tracecat

<Warning>
  Be careful when updating Tracecat.
  Do not accidentally overwrite or lose your existing `TRACECAT__SERVICE_KEY`, `TRACECAT__SIGNING_SECRET`, `TRACECAT__DB_ENCRYPTION_KEY`, and `USER_AUTH_SECRET` secrets.
  Losing these secrets will break your credentials and webhooks.
</Warning>

```bash
# 1. Download the latest env migration script and env template
curl -o env-migration.sh https://raw.githubusercontent.com/TracecatHQ/tracecat/<version>/env-migration.sh
curl -o .env.example https://raw.githubusercontent.com/TracecatHQ/tracecat/<version>/.env.example

# 2. Make the migration script executable and update your existing .env
chmod +x env-migration.sh && ./env-migration.sh

# 3. Download the latest Docker Compose file
curl -o docker-compose.yml https://raw.githubusercontent.com/TracecatHQ/tracecat/<version>/docker-compose.yml

# 4. Download the latest Caddyfile
curl -o Caddyfile https://raw.githubusercontent.com/TracecatHQ/tracecat/<version>/Caddyfile

# 5. Restart Tracecat with the updated configuration
docker compose up -d
```

The migration script creates a backup of your existing `.env` before rewriting it.
After the stack starts, verify that your containers are healthy and that you can sign in successfully.

## Scaling

Docker Compose runs all services on a single host.
These are recommended minimums for different workload sizes.

| Resource | Small | Standard | Production |
| :------- | :---- | :------- | :--------- |
| CPU | 8 cores | 16 cores | 32+ cores |
| RAM | 16 GB | 32 GB | 64+ GB |
| Storage | 20 GB SSD | 50 GB SSD | 100+ GB SSD |
| Docker | 26.0.0+ | 26.0.0+ | 26.0.0+ |
| Workflow starts/sec | ~5 | ~15 | ~40 |
| Concurrent seats | 1-10 | 10-50 | 50+ |
| Concurrent agents/sec | 1-2 | 5-10 | 10+ |

- Small: Development, testing, small teams (1-10 users)
- Standard: Mid-size teams (10-50 users), moderate workflow execution
- Production: Large teams (50+ users), high throughput

Memory is typically the constraining factor.
Workflow throughput is lower than dedicated Kubernetes deployments where services have guaranteed resources and can scale independently.

For production workloads that exceed single-host capacity, consider [converting to Docker Swarm](#convert-to-docker-swarm) or deploying on Kubernetes.

## Convert to Docker Swarm

Docker Swarm lets you scale individual services with resource limits and replicas across one or more nodes.

### Service resource recommendations

| Service | CPU (cores) | Memory | Replicas |
| :------ | ----------: | -----: | -------: |
| caddy | 0.25 | 256 Mi | 1 |
| api | 2 | 4 Gi | 2 |
| worker | 2 | 2 Gi | 4 |
| executor | 4 | 8 Gi | 4 |
| agent-worker | 2 | 2 Gi | 2 |
| agent-executor | 4 | 16 Gi | 2 |
| ui | 0.5 | 1 Gi | 2 |
| mcp | 1 | 1 Gi | 1 |
| postgres_db | 2 | 4 Gi | 1 |
| temporal | 4 | 8 Gi | 1 |
| temporal_postgres_db | 2 | 4 Gi | 1 |
| redis | 0.5 | 1 Gi | 1 |
| minio | 0.5 | 1 Gi | 1 |
| Total | ~25 | ~52 Gi | 23 |

<Info>
  Stateful services (`postgres_db`, `temporal_postgres_db`, `minio`, `redis`) must remain at 1 replica.
  Scaling these requires external managed services (e.g., RDS, ElastiCache) or specialized clustering.
</Info>

### Deploy with Docker Swarm

1. Initialize Swarm:

```bash
docker swarm init
```

2. Add `deploy` configuration to each service in your `docker-compose.yml`. For example, for the `api` service:

```yaml
services:
  api:
    # ... existing configuration ...
    deploy:
      replicas: 2
      resources:
        limits:
          cpus: "2"
          memory: 4G
        reservations:
          cpus: "2"
          memory: 4G
```

3. Deploy the stack:

```bash
docker stack deploy -c docker-compose.yml tracecat
```

4. Verify services are running:

```bash
docker stack services tracecat
```

## FAQ

For an overview of all services and networking, see [Architecture](/self-hosting/architecture).

<AccordionGroup>
  <Accordion title="Does Tracecat support Windows?">
    Yes but don't forget to set `PUBLIC_APP_URL` to `127.0.0.1` instead of `localhost` in your `.env` file.
  </Accordion>

  <Accordion title="Does Tracecat support WSL?">
    Yes but you'll need to set `PUBLIC_APP_URL` to the WSL IP address instead of `localhost`.
    Run `ip addr` to find your WSL IP address (usually 172.x.x.x).
  </Accordion>

  <Accordion title="I can see the landing page but not the login page">
    This suggests that the Tracecat UI is healthy but is unable to communicate with the Tracecat API.

    - Check that the `api` service is healthy
    - Check that this is not a CORS issue (see next FAQ)
    - Check that you changed `PUBLIC_APP_URL` and `PUBLIC_APP_PORT` to the correct address
    - Check the Chrome developer console for any errors
  </Accordion>

  <Accordion title="What is the CORS issue?">
    You must configure `PUBLIC_APP_URL` and `PUBLIC_API_URL` to the same domain that your browser is accessing the Tracecat UI from.
    Tracecat strictly enforces CORS to prevent XSS attacks.

    This error occurs when your browser cannot connect to Tracecat API via `PUBLIC_APP_URL`.
    Please check the following:

    - Do not set `PUBLIC_APP_URL` to `0.0.0.0`. Browsers cannot connect to this address. Use `localhost`, `127.0.0.1`, or your machine's actual IP address instead.
    - WSL users: Do not use `127.0.0.1`. Run `ip addr` to find your WSL IP address (usually `172.x.x.x`) and use that instead.
    - If your frontend is running on a different machine than the API, set `NEXT_PUBLIC_API_URL` to an address your browser can reach.
    - Run `docker compose ps` to verify all services are running.
    - Check the temporal service is healthy. Worker connectivity issues can sometimes affect login.
    - View container logs: `docker compose logs api` and `docker compose logs temporal`.
    - Check firewall and port forwarding if using a reverse proxy.

    For example, if you've deployed Tracecat into a VM and exposed it to your browser as `https://my-tracecat-instance.com`,
    you must set:
    - `PUBLIC_APP_URL` to `https://my-tracecat-instance.com`
    - `PUBLIC_API_URL` to `https://my-tracecat-instance.com/api`
  </Accordion>

  <Accordion title="What should PUBLIC_APP_URL and PUBLIC_API_URL be, and how do I fix login or CORS issues?">
    Set both values to the exact browser-reachable URLs for your deployment, including the scheme:

    - Local example:
      - `PUBLIC_APP_URL=http://localhost:8000`
      - `PUBLIC_API_URL=http://localhost:8000/api`
    - Reverse proxy or public domain example:
      - `PUBLIC_APP_URL=https://tracecat.example.com`
      - `PUBLIC_API_URL=https://tracecat.example.com/api`

    Use full URLs once.
    Do not prepend `http://` or `https://` twice, and do not use addresses that only work inside Docker such as `0.0.0.0` or internal container hostnames.

    If sign-in still fails:

    - Verify that the domain in your browser exactly matches `PUBLIC_APP_URL`.
    - Verify that `PUBLIC_API_URL` is the same origin plus `/api`.
    - Run `docker compose ps` and check that `api`, `worker`, and `temporal` are healthy.
    - Review `docker compose logs api` for startup, auth, or CORS errors.
  </Accordion>

  <Accordion title="I'm seeing 'No hosts available' errors from Temporal">
    Undersized Temporal clusters cause `No hosts available` errors and workflow execution failures.
    Ensure the Temporal server has at least 4 CPU cores and 8 GB of memory.

    Temporal also requires a well-provisioned PostgreSQL backend for adequate query throughput.
    The bundled `temporal_postgres_db` container is suitable for development and small deployments only.
    For production workloads, use a managed PostgreSQL instance (e.g., Amazon Aurora Serverless) for better QPS and reliability.
  </Accordion>

  <Accordion title="How do I configure SSL for production?">
    Tracecat ships with [Caddy](https://caddyserver.com/) as a reverse proxy.
    Caddy automatically provisions and renews TLS certificates from Let's Encrypt when configured with a domain name.

    <Steps>
      <Step title="Update environment variables">
        In your `.env` file, set `BASE_DOMAIN` to your domain and update the public URLs to use HTTPS:

        ```bash
        BASE_DOMAIN=tracecat.example.com
        PUBLIC_APP_URL=https://tracecat.example.com
        PUBLIC_API_URL=https://tracecat.example.com/api
        ```
      </Step>
      <Step title="Expose ports 80 and 443">
        Update the `caddy` service ports in your `docker-compose.yml`:

        ```yaml
        services:
          caddy:
            ports:
              - "80:80"
              - "443:443"
        ```

        Port 80 must remain open for the ACME HTTP-01 challenge. Port 443 serves HTTPS traffic.
      </Step>
      <Step title="Restart the stack">
        ```bash
        docker compose up -d
        ```

        Caddy will automatically obtain and renew certificates on startup.
      </Step>
    </Steps>
  </Accordion>
</AccordionGroup>
