# Completing a Docker Compose deployment

## Install Docker

We recommend that you start with a freshly installed OS on a virtual private server (VPS) that does not contain any other services.

Otherwise, you might encounter issues with occupied ports or incompatible libraries that this guide does not cover.

For details on how to install Docker, go to the [Docker documentation](https://docs.docker.com/engine/install/).

### Supported architectures

The supported CPU architectures are:

* `AMD64 (x86-64)`&#x20;
* `ARM64 (aarch64)`

This guide uses Ubuntu 24.04 LTS.

### Notes on the Docker installation

#### Standard installation that runs under root

This guide uses a standard Docker installation where the Docker daemon runs under root as the  `systemd` service. Containers that run unprivileged and application processes inside containers operate as non-root users.

This configuration allows exposing to containers additional Linux capabilities (syscalls) that are not available by default in more restricted environments, and that cannot be granted in [Docker Rootless mode](https://docs.docker.com/engine/security/rootless/). Fabricate relies on these low-level syscalls, such as `SYS_PTRACE`, for its JavaScript sandbox isolation.

This setup also allows binding to privileged ports (< 1024) and running a load balancer / proxy alongside other services without additional host-system configuration changes, such as modifying `net.ipv4.ip_unprivileged_port_start`.

#### Default runc runtime

This guide uses Docker with the default runtime `runc`.

Alternative runtimes that have more strict per-container microVM or user-space kernel isolation, such as `kata-runtime` (Kata Containers) or `runcs` (gVisor), are not supported, and might not work correctly, because they might not implement or expose all of the required syscalls.

For more information, go to [Docker runtimes](https://docs.docker.com/engine/daemon/alternative-runtimes/) in the Docker documentation.

## Pull Docker images

The Fabricate Docker image is available to Enterprise customers on [Quay.io](https://quay.io/).

This guide uses the `latest` tag, so that you always pull the latest available Fabricate version. For a production setup, we recommend that you instead pin Docker images to specific versions.

First, log in to the registry with the credentials that Tonic.ai provided:

```bash
docker login -u='<username>' -p='<password>' quay.io
```

Next, run the following command to pull the main application image:

```bash
docker pull quay.io/tonicai/fabricate:latest
```

Next, pull the application PDF sidecar image:

```bash
docker pull quay.io/tonicai/fabricate-html-to-pdf-sidecar:latest
```

<figure><img src="/files/i9rp6KVviHxDVQP3WGWR" alt=""><figcaption></figcaption></figure>

## Prepare the .env file

Fabricate requires several environment variables.

### Create the working directory

First, create a directory for the `.env` and `docker-compose.yml` template files.

```bash
mkdir -p ./fabricate
cd ./fabricate
```

### Copy the .env template

The template file is available in the GitHub repository <https://github.com/TonicAI/fabricate_docker_compose/blob/main/.template.env>. Copy the  `.template.env` file from the repository into the directory as `.env`. The template contains the available environment variables with descriptions.

The commented-out variables are not immediately needed.

### Configure SECRET\_KEY\_BASE

You must configure `SECRET_KEY_BASE`.

To generate a new secret key for your instance, run the following command:

```bash
docker run --rm quay.io/tonicai/fabricate:latest /fabricate/bin/rails secret
```

Save the output in `.env`:

{% code overflow="wrap" %}

```bash
SECRET_KEY_BASE="a941ffd00cffe765877685557323dbf5f23e266ee41c45240141941b53e6e417edbf9a016ef932d1d7e208e1de2a2010d482eab03"
```

{% endcode %}

### Configure FABRICATE\_HOST

The `FABRICATE_HOST` environment variable defines the host for which Fabricate accepts requests and generates links. Fabricate rejects requests for other hosts.

This should be the domain where you host Fabricate. If you do not have one yet, you can keep `localhost:3000` for now and change it later.

For example:

```bash
FABRICATE_HOST="fabricate.localhost:3000"
```

### Configure FABRICATE\_ADMIN\_EMAIL

The `FABRICATE_ADMIN_EMAIL` environment variable defines the email address of the first admin user.

When a user signs up for Fabricate with this email address, they receive access to the **Admin Console** and are granted full control over the Fabricate instance.

To ensure that you can complete the first signup, this must be a working email address.

For example:

```bash
FABRICATE_ADMIN_EMAIL=admin@fabricate.localhost
```

### Configure FABRICATE\_MAIL\_\*

The `FABRICATE_MAIL_*` environment variables configure the SMTP service for outgoing email messages from Fabricate.

You can use:

* Amazon Simple Email Service (Amazon SES) with SMTP credentials
* Amazon SES with an IAM role
* A dedicated mailbox for Fabricate under your organization
* Another SMTP provider

If you do not configure this correctly, you will not be able to sign up to Fabricate, because you will not receive the confirmation email message.

For example:

```bash
FABRICATE_MAIL_ADDRESS=email-smtp.us-west-2.amazonaws.com
FABRICATE_MAIL_PORT=587
FABRICATE_MAIL_USER_NAME=
FABRICATE_MAIL_PASSWORD=
FABRICATE_MAIL_FROM=no-reply@fabricate.localhost
```

Note that if you SMTP server enforces it,  `FABRICATE_MAIL_FROM` must match the mailbox email address.

To improve security and deliverability, and to reduce the chance that email messages are flagged as spam, we also recommend that you set up SPF, DKIM, and DMARC DNS records for your domain.

### Configure your LLM Provider

Fabricate AI features rely on an external LLM provider. You must configure a connection to either Amazon Bedrock, Anthropic, or Azure AI Foundry.

In the `.env` file, uncomment and configure the settings for the provider that you want to use. The settings include the API key to use for that provider.

For example, to use Anthropic:

```bash
FABRICATE_ANTHROPIC_API_KEY="sk-asdf"
FABRICATE_ANTHROPIC_BASE_URL="https://api.anthropic.com"
```

### Optional: Configure FABRICATE\_BLOCK\_STORAGE\_SERVICE

By default, Fabricate uses local storage for generated assets, PDF files, and similar data.

To use an external Amazon S3-compatible storage service, in the `.env` file, configure the `FABRICATE_BLOCK_STORAGE_SERVICE` environment variable.

To use Amazon S3 instead:

1. Set:

```bash
FABRICATE_BLOCK_STORAGE_SERVICE="amazon"
```

2. Configure the Amazon S3 details:

```bash
FABRICATE_S3_BUCKET="fabricate"
FABRICATE_AWS_REGION="us-east-1"
# FABRICATE_AWS_ACCESS_KEY_ID=
# FABRICATE_AWS_SECRET_ACCESS_KEY=
```

## Prepare docker-compose.yml

The Docker Compose file is available in the GitHub repository <https://github.com/TonicAI/fabricate_docker_compose/blob/main/docker-compose.yaml>. Copy the `docker-compose.yaml` template into the `./fabricate` folder with the `.env` file.

The template contains prefilled database credentials, connection strings, storage paths, socket paths, and ports. These values are loaded from the previously created `.env` file.

However, you must change the:

* The PostgreSQL credentials.
* The Redis credentials.

## Start the services

For the first run, start the services in the foreground:

```bash
docker compose up
```

If the containers start cleanly, stop them with `Ctrl+C`.

Next, run the services in detached mode:

```bash
docker compose up -d
```

Persistent data is stored locally in Docker volumes.

<figure><img src="/files/oYdC8GehGb9l6y5v9VAE" alt=""><figcaption></figcaption></figure>

Fabricate now runs and listens on HTTP port 3000 on the instance.

## Configure the load balancer

To obtain an HTTPS certificate and expose it on the internet, you configure an HTTP/s load balancer.

You can use either:

* AWS Application Load Balancer (ALB).
  * Uses an AWS Certificate Manager (ACM) certificate.
  * Managed by AWS.
  * DNS points to ALB.
* Caddy
  * Uses a Let's Encrypt certificate.
  * Runs on the same VPS.
  * DNS points to the VPS.

### AWS ALB

This is the recommended setup on AWS.

1. In AWS Certificate Manager (ACM), issue a certificate for your hostname, such as `fabricate.example.com` , using a DNS or Email challenge.
2. Create new target group (at **EC2 / Load Balancing / Target Groups**) with:
   * Protocol `HTTP`
   * Port `3000`
   * Health check path `/up`
3. Register the used Amazon EC2 instance in that target group.
4. Create a new application load balancer under **EC2 / Load Balancing / Load balancers** with:
   * HTTPS (443) forwarding to the target group
   * Optional HTTP (80) redirecting to HTTPS
   * Previously issued ACM certificate for your domain
   * Previously created target group with the Amazon EC2 instance
5. Allow `443` on the ALB security group.
6. Allow `3000` on the instance only from the ALB security group.
7. Point DNS for your hostname to the ALB CNAME / A / AAAA.

Update `.env`:

```bash
FABRICATE_HOST="fabricate.example.com"
```

To apply the changes, restart the stack:

```bash
docker compose up -d --force-recreate
```

### Caddy on the same VPS

Use this option when you want Transport Layer Security (TLS) termination on the server itself.

1. Add an `A` or `AAAA` record for your hostname (such as `fabricate.example.com`) that points to the server IP address.
2. Allow inbound TCP `80` and `443` connections.\
   \
   Note that publishing **`80:80`** and **`443:443`** requires a standard rootful Docker install on a VPS, such as Ubuntu with Docker Engine from Docker’s packages.\
   \
   If you use rootless Docker, binding those host ports often fails unless you allow unprivileged low ports on the host. For example, `net.ipv4.ip_unprivileged_port_start=0`.
3. Add the following additional `caddy` service to your existing  `docker-compose.yml` file:

```yaml
services:
  caddy:
    image: caddy:latest
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    extra_hosts:
      - "host.docker.internal:host-gateway"
    networks:
      - fabricate_internal
      
volumes:
  caddy_data:
  caddy_config:
```

4. In the same directory, create a `Caddyfile` configuration with a proxy to the `http://web:3000` container.\
   \
   Caddy uses ACME HTTP challenge to automatically issue and renew the certificate for your domain.

```
fabricate.example.com {
  reverse_proxy http://web:3000
  tls {
    protocols tls1.2 tls1.3
  }
}
```

5. Update `.env`:

```bash
FABRICATE_HOST="fabricate.example.com"
```

6. To apply the changes, restart the stack:

```bash
docker compose -f docker-compose.yml up -d --force-recreate
```

## Create the admin account

Next, go to `https://fabricate.example.com` .

Sign up as a new user. Use the email address that you configured as the value of `FABRICATE_ADMIN_EMAIL`.

After you activate the account from the confirmation email message, you can sign in and use the **Admin Console**.

## Next steps

Your Fabricate instance is now ready to accept traffic.

For more information about Fabricate configuration options, go to [Configuring Fabricate](/fabricate/self-hosting-fabricate/configuring-fabricate.md).

For information on how to use the the **Admin Console** to manage your Fabricate instance, go to [Using the Admin Console](/fabricate/self-hosting-fabricate/using-the-admin-console.md).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.tonic.ai/fabricate/self-hosting-fabricate/deploying-and-managing-a-self-hosted-instance/using-docker-compose/completing-a-docker-compose-deployment.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
