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

# Builds

> Define how your code is built: managed base environments or custom Dockerfiles.

Builds define how to produce artifacts. Services and crons reference builds.

```hcl specific.hcl theme={null}
build "api" {
  base = "node"
  command = "npm run build"
}
```

## Configuration

One of the following is required (mutually exclusive):

* `base` - base environment: `"node"`, `"python"`, `"go"`, `"rust"`, or `"java"`. For anything else, use a custom Dockerfile.
* `dockerfile` - path to a custom Dockerfile, relative to `specific.hcl`.

Optional fields:

* `command` - build command to run after dependencies are installed (for example, `npm run build` or `go build -o api`). Only valid with `base`, not with `dockerfile`.
* `root` - working directory for build commands, relative to `specific.hcl`. Defaults to `"."`. Sets the `WORKDIR` in the generated Dockerfile; services that reference this build inherit its root, and dependency detection looks here.
* `context` - Docker build context scope, relative to `specific.hcl`. Defaults to `"."`. Controls what files are available to `COPY` and what's included in the deployment tarball.
* `env` - environment variables available during the build, passed as Docker build args. See [Build-time environment variables](#build-time-environment-variables).

## Automatic dependency installation

Specific detects your dependency manager (from the lockfile) and installs dependencies before running your build command. Installs are cached, so rebuilds are fast when only source code changes. For projects that only need dependencies installed, omit `command`:

```hcl specific.hcl theme={null}
build "api" {
  base = "node"
}
```

Run `specific docs builds` for the exact per-base detection and install behavior.

## Build-time environment variables

Use `env` to pass variables during the build step. This is useful for frameworks like Next.js that inline environment variables at build time.

```hcl specific.hcl theme={null}
build "web" {
  base    = "node"
  command = "npm run build"

  env = {
    NEXT_PUBLIC_API_URL = "https://${service.api.public_url}/v1"
    NODE_ENV            = "production"
  }
}
```

Supported reference types:

* String literals (for example, `"production"`).
* `service.<name>.public_url` - the public domain of another service.
* String interpolation with `${service.<name>.public_url}`.

Since `public_url` doesn't include a scheme, use interpolation to compose full URLs. Other references (secrets, config, databases) are not available at build time.

## Monorepos

Use `root` to set where build commands run. This applies to both the build command and dependency detection:

```hcl specific.hcl theme={null}
build "backend" {
  base = "node"
  root = "packages/backend"
  command = "npm run build"
}

build "frontend" {
  base = "node"
  root = "packages/frontend"
  command = "npm run build"
}
```

### `root` vs `context`

The two are orthogonal:

* `root` = where commands run (working directory).
* `context` = what files are available (Docker build context / tarball scope).

By default, `context` is the `specific.hcl` directory. Set it only to widen the scope, for example to include shared libraries in a parent directory:

```hcl specific.hcl theme={null}
# specific.hcl is in apps/myapp/, but shared libs are in libs/
build "api" {
  base = "node"
  root = "packages/api"
  context = "../"          # Include parent dir so shared libs are available
}
```

## Custom Dockerfile

For full control over the build, provide your own Dockerfile instead of `base`:

```hcl specific.hcl theme={null}
build "api" {
  dockerfile = "Dockerfile"
}
```

When using a custom Dockerfile:

* `command` is not allowed - define build steps in the Dockerfile itself.
* `context` still controls the Docker build context.
* `env` vars are passed as Docker build args; consume them with `ARG` instructions.
* The service `command` still overrides the container `CMD` at runtime.

```hcl specific.hcl theme={null}
build "web" {
  dockerfile = "docker/Dockerfile.prod"
  context = "."

  env = {
    NEXT_PUBLIC_API_URL = service.api.public_url
    NODE_ENV            = "production"
  }
}
```

```dockerfile Dockerfile theme={null}
FROM node:20-slim
ARG NEXT_PUBLIC_API_URL
ARG NODE_ENV
WORKDIR /app
COPY . .
RUN npm ci && npm run build
CMD ["npm", "start"]
```

## Local development

The `dev` block overrides the build command during `specific dev`. If no `dev` block is defined, the build is skipped in development; most dev servers (like `npm run dev`) handle building themselves.

```hcl specific.hcl theme={null}
build "spa" {
  base = "node"
  command = "npm run build"

  dev {
    command = "npm run build:watch"
  }
}
```

For compiled languages (Go, Rust, Java), a `dev` block is almost always needed; without it, no binary is produced locally. Use a tool that rebuilds on file changes:

```hcl specific.hcl theme={null}
build "api" {
  base = "go"
  command = "go build -o api ./cmd/api"

  dev {
    command = "go run ./cmd/api"
  }
}
```

## Production

During `specific deploy`, builds run in the cloud in parallel, after a local test run catches errors early. See [Deployments](/concepts/deployments) for the full pipeline.
