# The disco.json File

> Configuration reference for services, volumes, and deployment strategies.

The `disco.json` file is the heart of your project’s configuration. It sits at the root of your repository and tells Disco how to build your application, which services to run, and how to route traffic.

## The Basics

At its absolute minimum, `disco.json` defines a single **web** service and the port it listens on.

If your repository has a `Dockerfile` in the root, this is all you need. Disco will build the image, start the container, and route public HTTP traffic to port `8000`.

```json
{
  "version": "1.0",
  "services": {
    "web": {
      "port": 8000
    }
  }
}
```

* **`web`**: This service name is reserved. It is the only service that automatically receives traffic from your public domain (via HTTP/HTTPS).
* **`port`**: The internal port your application is listening on inside the container.

***

> **Environment Variables**
>
> **Do not put environment variables in `disco.json`.**
>
> This file is committed to Git, and secrets (API keys, database passwords) should never be in version control.
>
> Instead, set environment variables using the [Disco Dashboard](https://dashboard.disco.cloud/) or the CLI command: `disco env:set KEY=VALUE`

***

## Persisting Data (Volumes)

By default, Docker containers are ephemeral. If you restart your app or deploy a new version, any files written inside the container are lost. To keep data (like SQLite databases, user uploads, or logs), you must use **Volumes**.

```json
{
  "version": "1.0",
  "services": {
    "web": {
      "port": 8000,
      "volumes": [
        {
          "name": "sqlite-data",
          "destinationPath": "/data"
        }
      ]
    }
  }
}
```

* **`name`**: An identifier for the volume. If multiple services (e.g., web and worker) use the same name, they will share the same data.
* **`destinationPath`**: The folder path inside the container where this volume is mounted.

***

## Adding Workers

Many applications need background processes to handle tasks like sending emails or processing queues. You can define additional services under the `services` block.

Workers usually share the same Docker image as the `web` service but run a different command.

```json
{
  "version": "1.0",
  "services": {
    "web": {
      "port": 8000,
      "volumes": [
        {
            "name": "sqlite-data",
            "destinationPath": "/data"
        }
      ]
    },
    "worker": {
      "command": "python worker.py",
      "volumes": [
        {
            "name": "sqlite-data",
            "destinationPath": "/data"
        }
      ]
    }
  }
}
```

In this example, the `worker` service reuses the code built for `web` but overrides the startup command. Note that we also attached the `sqlite-data` volume so the worker can access the same database as the web server.

***

## Cron Jobs

Disco can run scheduled tasks automatically. Instead of keeping a container running permanently, a `cron` service starts, runs a command, and then exits, following a schedule.

```json
{
  "version": "1.0",
  "services": {
    "web": { "port": 8000 },
    "cleanup-job": {
      "type": "cron",
      "schedule": "0 0 * * *",
      "command": "node cleanup.js"
    }
  }
}
```

* **`type`**: Must be set to `"cron"`.
* **`schedule`**: A standard 5-part cron expression (e.g., `*/10 * * * *` for every 10 minutes).
* **`command`**: The command to execute.

***

## Deployment Hooks

Sometimes you need to run a script *during* deployment, but before the new version of your app goes live. The most common use case is running database migrations.

Disco recognizes services starting with `hook:deploy:start:before` as pre-deployment tasks.

```json
{
  "version": "1.0",
  "services": {
    "web": {
      "port": 8000,
      "volumes": [{ "name": "db", "destinationPath": "/data" }]
    },
    "hook:deploy:start:before": {
      "type": "command",
      "command": "python manage.py migrate",
      "volumes": [{ "name": "db", "destinationPath": "/data" }]
    }
  }
}
```

If this command fails (returns a non-zero exit code), the deployment is aborted, and the old version of your application keeps running.

***

## Static Sites & Generators

If you don’t have a backend server and just want to serve HTML/CSS/JS, you can use the `static` or `generator` types.

### Pure Static Files

If you just have a folder of files named `public` in your repo (no build step required):

```json
{
  "version": "1.0",
  "services": {
    "web": {
      "type": "static",
      "publicPath": "public"
    }
  }
}
```

### Static Site Generators (SSG)

If you use tools like Astro, Next.js (static export), or Jekyll, you need to build the site first. The `generator` type builds your Docker image, runs it to generate files, and then serves the output.

```json
{
  "version": "1.0",
  "services": {
    "web": {
      "type": "generator",
      "publicPath": "dist"
    }
  }
}
```

* **`publicPath`**: The folder *inside* the container where the build output is located (e.g., `dist`, `build`, `out`).

***

## Easy Mode (Pre-built Images)

You don’t always need a `Dockerfile`. You can run standard software (like databases or message brokers) by specifying an image directly from Docker Hub.

```json
{
  "version": "1.0",
  "services": {
    "web": {
      "image": "getmeili/meilisearch:v1.13",
      "port": 7700,
      "volumes": [
        {
          "name": "meili_data",
          "destinationPath": "/meili_data"
        }
      ]
    }
  }
}
```

***

## Advanced Configuration

### Health Checks

To ensure zero-downtime (“blue green”) deployments, Disco needs to know when your app is actually ready to receive traffic. Without a specific health check, Disco will try to connect to your app’s port (`8080` in the example below), but a more robust way is to define a custom command.

```json
{
  "version": "1.0",
  "services": {
    "web": {
      "port": 8080,
      "health": {
          "command": "curl -f http://localhost:8080/health || exit 1"
      }
    }
  }
}
```

### Custom Dockerfiles (Monorepos)

If your `Dockerfile` isn’t in the root, or if you have multiple services needing different Dockerfiles, use the `images` block.

```json
{
    "version": "1.0",
    "services": {
        "web": { "port": 8000, "image": "backend" },
        "worker": { "command": "node worker.js", "image": "worker" },
    },
    "images": {
        "backend": {
            "dockerfile": "api/Dockerfile",
            "context": "api"
        },
        "worker": {
            "dockerfile": "worker/Dockerfile",
            "context": "worker"
        }
    }
}
```

### Exposing Non-HTTP Ports

If you need to expose a port for TCP/UDP traffic (e.g., for a game server or websocket service), use `publishedPorts`. These ports will be exposed “as is” on your server’s public IP address, and won’t be routed through HTTP/HTTPS.

```json
{
    "version": "1.0",
    "services": {
        "web": {
            "port": 3000,
            "command": "node server.cjs"
        },
        "websocket": {
            "command": "npx y-websocket",
            "publishedPorts": [{
                "publishedAs": 1234,
                "fromContainerPort": 1234,
                "protocol": "tcp"
            }]
        }
    }
}
```

* **`publishedAs`**: The port on the host machine (public internet).
* **`fromContainerPort`**: The port inside the container.

***

## Complete Reference

Here is a complex example showing multiple features in one file.

```json
{
    "version": "1.0",
    "services": {
        "web": {
            "port": 8000,
            "health": { "command": "curl -f http://localhost:8000/ || exit 1" },
            "volumes": [{ "name": "data", "destinationPath": "/data" }]
        },
        "worker": {
            "command": "python worker.py",
            "volumes": [{ "name": "data", "destinationPath": "/data" }]
        },
        "cleanup": {
            "type": "cron",
            "schedule": "0 4 * * *",
            "command": "python cleanup.py"
        },
        "hook:deploy:start:before": {
            "type": "command",
            "command": "python manage.py migrate",
            "volumes": [{ "name": "data", "destinationPath": "/data" }]
        }
    }
}
```

### Service Fields Table

| Field               | Type    | Description                                                          |
| ------------------- | ------- | -------------------------------------------------------------------- |
| `type`              | string  | `container` (default), `static`, `generator`, `cron`, `command`.     |
| `port`              | number  | Internal port the container listens on. Required for `web`.          |
| `image`             | string  | Name of image to use. If omitted, builds from `Dockerfile`.          |
| `command`           | string  | Override the Docker `CMD`.                                           |
| `volumes`           | array   | List of `{ name, destinationPath }` objects.                         |
| `schedule`          | string  | Cron expression (only for `type: "cron"`).                           |
| `health`            | object  | Object containing `{ command }` for health checks.                   |
| `publicPath`        | string  | Folder to serve for `static` or `generator` types.                   |
| `publishedPorts`    | array   | Map ports to host: `[{ publishedAs, fromContainerPort, protocol }]`. |
| `exposedInternally` | boolean | If true, other projects on the server can reach this service.        |