# Services

Run a service of your own next to the application, on an image you build, reached by the application with a secret only the two hold.


Some applications need a program next to them that is not PHP: a model that recognises names in text, a converter, a search engine. A service runs it in a container of its own, in the same Scaleway project and region as the application.

## In brewless.yml

Name the service under the environment, with the directory that holds its Dockerfile:

```yaml brewless.yml
environments:
  production:
    services:
      gliner:
        build: services/gliner
        memory: 4096
        cpu: 2240
        timeout: 300
        min: 0
        max: 5
        concurrency: 2
```

A service name has lowercase letters and digits, starts with a letter and has no dashes; `web`, `release` and `worker` are taken. Without sizes a service gets 1024 MB, 560 thousandths of a vCPU, a timeout of 60 seconds and at most one replica, and it sleeps when nobody calls it (`min: 0`). See [brewless.yml](/reference/brewless-yml) for the bounds.

## Scaling

A service scales like the web container: Scaleway starts replicas as requests come in, between `min` and `max`. `concurrency` is how many requests one replica takes at once before Scaleway starts another (1 to 80). Without it a container Brewless made keeps Scaleway's own number, 50, so parallel requests wait on one replica. Set it to what one replica handles at the same time: a service with two worker processes takes `concurrency: 2`, and with `max: 5` ten requests run side by side on five replicas. `min: 0` lets the service sleep when nobody calls it; `min: 1` keeps one replica warm, which costs while it runs.

## Putting it live

```bash
brewless service:push production gliner
```

This builds the Dockerfile with Docker on your own machine, for `linux/amd64`, and uploads the image straight into a private bucket of your Scaleway project. A job in that project pushes it into your project's own registry, and Brewless makes the container `gliner` next to the web container, or gives the one there the new image. The image is never public and never passes through Brewless. The same image is not pushed twice: its tag is the identifier Docker gave it.

The container listens on the port in the `PORT` variable (8080). Brewless gives it one more variable, `SERVICE_TOKEN`: a secret it makes once in your Secret Manager and never replaces. The service has to refuse every request that does not carry it.

> [!NOTE]
> Brewless does not build services in your project, as it does your application: a job there runs without privileges and cannot run a Dockerfile. That is why the image of a service is built where Docker is: on your machine.

## How the application reaches it

The next deploy gives the web, release and worker containers two variables per service: `GLINER_URL`, the address of the service's container, and `GLINER_TOKEN`, the same secret the service holds. Send the secret with every call, for example as `Authorization: Bearer <GLINER_TOKEN>`. The application calls the service directly; Brewless is not in between.

A deploy is refused while `brewless.yml` names a service that does not run yet: push it first. The variables go next to the environment's own variables, so a deploy with a service also needs those kept through Brewless (push them once with `brewless env:push`).

Changing the sizes or the scaling of a service in `brewless.yml` takes effect at the next `brewless service:push`, also when the image did not change: the container gets the new settings. A deploy of the application does not change them. `brewless plan` shows them next to what the container has, marked as applied by `brewless service:push`. Taking a service out of the file stops the application getting its variables at the next deploy; the container itself keeps running until you remove it in your Scaleway console.

## A private service

By default a service's container has a public address, like every Serverless Container: anyone who knows it can send a request, and only the secret keeps them out. `private: true` takes that address away:

```yaml brewless.yml
environments:
  production:
    network: private
    services:
      gliner:
        build: services/gliner
        private: true
```

At `brewless service:push` the container joins the project's Private Network (see [A Private Network](/applications/applications-and-environments#a-private-network)), gets a private endpoint on it, and its default public endpoint is switched off: from the internet its address answers 404, with or without the secret. The application then reaches it on the private endpoint, which Scaleway names `http://<container id>.<network id>.internal`, and the next deploy gives that address as `GLINER_URL`. The secret stays: send it as before.

- A private service needs `network: private` (or a `cache`) on its environment: the web, release and worker containers have to be on the same network to reach it. Without it the file is refused.
- The push is refused while one of those containers is not on the network yet. Deploy once with `network: private` first, so they join it.
- Between the push and the next deploy the application still holds the old public address, which no longer answers. Deploy right after the push.
- The private endpoint speaks HTTP inside the network. HTTPS on it needs Scaleway's own certificate authority in the application.
- Taking `private: true` out again and pushing gives the container its public address back; it stays on the network.
