# Service settings

> The settings each service takes from vallic.yaml, what they do, and what stays with the platform.

The services your site runs beside it — the database, the cache, Nginx, PHP
itself — each take a handful of settings: how much memory the database keeps
for its cache, the largest upload Nginx lets through, how long a PHP request may
run. You set them in [`vallic.yaml`](configuration.md), under the service they
belong to.

```yaml
runtime:
  php: '8.4'

services:
  - mariadb:
      version: '11.8'
      environment:
        MYSQL_MAX_ALLOWED_PACKET: 256M
        MYSQL_SLOW_QUERY_LOG: '1'
  - nginx:
      version: '1.31'
      environment:
        NGINX_CLIENT_MAX_BODY_SIZE: 64m
  - php:
      version: '8.4'
      environment:
        PHP_MAX_EXECUTION_TIME: '120'
        PHP_UPLOAD_MAX_FILESIZE: 64M
        PHP_POST_MAX_SIZE: 64M
```

Every entry under `services` needs a version, `php` included. Which PHP runs is
still `runtime.php` — see [Configuration](configuration.md#runtime) — so keep
the two the same.

These are not project variables. A project variable is something your
application reads; a service setting changes the service. They live in the
repository because they belong to the code: the upload limit your site was
built around is part of the site, not something to remember to set on each
new environment.

## The rules

**Only the settings listed below.** Each service takes a short list, and a
deploy that sets anything else refuses, naming the variable and what the
service does take. Nothing is accepted and quietly ignored, so a setting that
reaches a deploy is a setting that is in effect.

**Each value is checked.** A setting that takes a number takes a number, a size
takes a size, and a pattern that ends up in the service's own configuration
file takes only the characters a pattern needs. A value of the wrong shape
refuses the deploy, naming it. Everything is one line of text, at most 1,024
characters, and a `$` is a dollar sign: values are never expanded, so one
setting cannot read another, or anything else on the machine.

**They take effect on the next deploy.** A service whose settings changed is
restarted with them before your code lands. Delete a line and the next deploy
puts the default back.

**Background work gets the application's settings.** The queue worker, your
[workers](configuration.md#cron-and-workers) and your scheduled jobs are your
application run differently. A job given less time or memory than the site
would be the one that fails, so they run with whatever you set on `php`.

**Memory is sized from your machine.** The settings that decide how much memory
a service holds — the database's buffer pool, the cache's limit, Solr's heap,
Varnish's storage, PHP's APCu — are set from what you run on, not left at the
image's defaults. A service with a machine of its own takes most of it: a cache
alone on its machine gets 80% of it, the database 60%. One that shares a
machine with your site gets a share of your plan instead, so it can never grow
into the memory your site needs. Those of them listed below — the buffer pool,
PostgreSQL's shared buffers, Solr's heap, APCu — you can set lower. A higher
value is lowered to your share. The cache's limit and Varnish's storage are not
settings at all: they are only ever sized.

**Production shows no errors on the page.** On a production environment PHP
does not print errors, startup errors or failed assertions into the response;
they still go to [the logs](logs.md). Other environments behave as the image
does. Set `PHP_DISPLAY_ERRORS` if you want something else.

**PHP's memory limit is held to your plan.** `PHP_MEMORY_LIMIT` goes through
the same rule as `runtime.memory_limit`: anything above what your plan allows
is lowered to it. If the file sets both, `runtime.memory_limit` wins.

**Timeouts are a chain.** A slow request passes through Nginx and, if you run
it, Varnish before it reaches PHP, and whichever gives up first answers. Raise
`PHP_MAX_EXECUTION_TIME` and `NGINX_FASTCGI_READ_TIMEOUT` together. With Varnish
in front, a page still has 60 seconds to start answering, whatever the other
two say. Work that takes longer belongs in a [worker](configuration.md#cron-and-workers).

**Your code is never checked for changes while it runs.** PHP caches compiled
code and would normally check every file for changes on every request. Your
code does not change under a running site: a deploy switches to the new
release and reloads PHP, which empties the cache. So the check is off
everywhere, and there is no setting for it.

## Every setting, by service

A service missing from this list takes no settings. Its page says why.

### [Go](stack-golang.md) — `golang`

| Variable | What it does | Unless you set it |
| --- | --- | --- |
| `GOMEMLIMIT` | A soft memory limit for the Go runtime, such as `900MiB`. | the image's |
| `GOMAXPROCS` | How many threads run Go code at once. | the image's |

### [MariaDB](stack-mariadb.md) — `mariadb`

| Variable | What it does | Unless you set it |
| --- | --- | --- |
| `MYSQL_MAX_ALLOWED_PACKET` | The largest single query or row, such as `256M`. | `256M` |
| `MYSQL_INNODB_BUFFER_POOL_SIZE` | The database's cache of tables and indexes. Sized from the database's share of the machine unless you set it lower. | sized from your machine |
| `MYSQL_MAX_CONNECTIONS` | Connections at once. Each one costs memory whether it is busy or not. | `100` |
| `MYSQL_SLOW_QUERY_LOG` | `1` to log queries slower than `MYSQL_LONG_QUERY_TIME`. | `OFF` |
| `MYSQL_LONG_QUERY_TIME` | Seconds after which a query counts as slow. | `2` |

### [Meilisearch](stack-meilisearch.md) — `meilisearch`

| Variable | What it does | Unless you set it |
| --- | --- | --- |
| `MEILI_MAX_INDEXING_MEMORY` | Memory Meilisearch may use while indexing. | the image's |
| `MEILI_LOG_LEVEL` | How much Meilisearch logs. | `INFO` |

### [MySQL 8](stack-mysql.md) — `mysql`

| Variable | What it does | Unless you set it |
| --- | --- | --- |
| `MYSQL_MAX_ALLOWED_PACKET` | The largest single query or row, such as `256M`. | `256M` |
| `MYSQL_INNODB_BUFFER_POOL_SIZE` | The database's cache of tables and indexes. Sized from the database's share of the machine unless you set it lower. | sized from your machine |
| `MYSQL_MAX_CONNECTIONS` | Connections at once. Each one costs memory whether it is busy or not. | `100` |
| `MYSQL_SLOW_QUERY_LOG` | `1` to log queries slower than `MYSQL_LONG_QUERY_TIME`. | the image's |
| `MYSQL_LONG_QUERY_TIME` | Seconds after which a query counts as slow. | the image's |

### [Nginx](stack-nginx.md) — `nginx`

| Variable | What it does | Unless you set it |
| --- | --- | --- |
| `NGINX_CLIENT_MAX_BODY_SIZE` | The largest request body Nginx passes on, such as `64m`. Uploads bigger than this never reach PHP. | `32m` |
| `NGINX_KEEPALIVE_TIMEOUT` | How long an idle browser connection is kept open. | `75s` |
| `NGINX_FASTCGI_READ_TIMEOUT` | How long Nginx waits for PHP to answer, in seconds. | `900` |
| `NGINX_GZIP_COMP_LEVEL` | Compression level, 1 to 6. Higher saves bytes and costs CPU on every response. | `1` |
| `NGINX_STATIC_EXPIRES` | How long browsers may cache static files, such as `7d`. | `1y` |
| `NGINX_ERROR_LOG_LEVEL` | How much Nginx logs: `error`, `warn` or `notice`. | `error` |
| `NGINX_DRUPAL_NOT_FOUND_REGEX` | Drupal presets only: paths matching this regex are answered with 404, so source files such as `composer.json` or `.yml` are never served. | the image's |
| `NGINX_WP_NOT_FOUND_REGEX` | WordPress preset only: paths matching this regex are answered with 404, so files such as `composer.json` or `.sql` dumps are never served. | the image's |

### [Node.js](stack-nodejs.md) — `nodejs`

| Variable | What it does | Unless you set it |
| --- | --- | --- |
| `NODE_OPTIONS` | Flags for Node itself, such as `--max-old-space-size`. Debugger flags are refused. | the image's |

### [OpenSMTPD](stack-opensmtpd.md) — `opensmtpd`

| Variable | What it does | Unless you set it |
| --- | --- | --- |
| `RELAY_HOST` | The SMTP server to send through. Without one, mail leaves from the machine's own address. | the image's |
| `RELAY_PORT` | Its port, usually 587. | `587` |
| `RELAY_PROTO` | How to connect to it, such as `smtp+tls`. | `smtp+tls` |
| `RELAY_USER` | The user to log in as. The password is a secret project variable, `RELAY_PASSWORD`, never a line in the file. | the image's |
| `OPENSMTPD_MAX_MESSAGE_SIZE` | The largest message accepted, such as `35M`. | `35M` |
| `OPENSMTPD_EXPIRE` | How long undeliverable mail is retried before it bounces. | `4d` |
| `OPENSMTPD_BOUNCE_WARN` | When the sender is warned that a message is still undelivered. | `1h, 6h, 2d` |

### [PHP-FPM](stack-php.md) — `php`

| Variable | What it does | Unless you set it |
| --- | --- | --- |
| `PHP_MEMORY_LIMIT` | Memory one request may use. Held to what your plan allows, and `runtime.memory_limit` wins when the file sets both. | `512M` |
| `PHP_MAX_EXECUTION_TIME` | Seconds a request may run. Nginx and Varnish keep their own clocks. | `120` |
| `PHP_POST_MAX_SIZE` | The largest request body PHP accepts. At least `PHP_UPLOAD_MAX_FILESIZE`, and no more than Nginx's `NGINX_CLIENT_MAX_BODY_SIZE`. | `32M` |
| `PHP_UPLOAD_MAX_FILESIZE` | The largest single uploaded file. | `32M` |
| `PHP_OPCACHE_MEMORY_CONSUMPTION` | Megabytes for compiled code. Raise it when a large codebase no longer fits. | `128` |
| `PHP_APCU_SHM_SIZE` | Shared memory for APCu, the in-process cache. Sized from your machine unless you set it lower. | sized from your machine |
| `PHP_FPM_PM_MAX_CHILDREN` | Requests served at once. Each may use up to the memory limit, so this times that has to fit the machine. | `8` |
| `PHP_DISPLAY_ERRORS` | Whether errors are printed into pages. Off unless you turn it on, and it should stay off anywhere a visitor can reach. | `Off` on production, `On` elsewhere |
| `PHP_MAX_INPUT_VARS` | How many input variables one request may carry. Large forms — Drupal's permissions page, menu editors — need more than the default. | `2000` |

### [PostgreSQL](stack-postgres.md) — `postgres`

| Variable | What it does | Unless you set it |
| --- | --- | --- |
| `POSTGRES_MAX_CONNECTIONS` | Connections at once. | `100` |
| `POSTGRES_SHARED_BUFFERS` | PostgreSQL's own cache. Sized from the database's share of the machine unless you set it lower. | sized from your machine |
| `POSTGRES_WORK_MEM` | Memory for one sort or hash — per operation, per connection, so a small number goes a long way. | `5MB` |

### [RabbitMQ](stack-rabbitmq.md) — `rabbitmq`

| Variable | What it does | Unless you set it |
| --- | --- | --- |
| `RABBITMQ_VM_MEMORY_HIGH_WATERMARK` | The share of its memory at which RabbitMQ stops accepting messages, such as `0.6`. Of the queue machine when it has one, of your plan otherwise. | the image's |

### [Redis](stack-redis.md) — `redis`

| Variable | What it does | Unless you set it |
| --- | --- | --- |
| `REDIS_MAXMEMORY_POLICY` | What is evicted when the cache is full: `allkeys-lru` suits a cache, `noeviction` a store that must not lose keys. | `allkeys-lru` |
| `REDIS_DATABASES` | How many numbered databases exist. | `16` |

### [Solr](stack-solr.md) — `solr`

| Variable | What it does | Unless you set it |
| --- | --- | --- |
| `SOLR_HEAP` | Java heap for Solr, such as `1g`. Sized from the search machine unless you set it lower. | sized from your machine |
| `SOLR_MODULES` | Solr modules to load, comma-separated, such as `extraction,langid`. | `extraction,langid,ltr,analysis-extras` |

### [Valkey](stack-valkey.md) — `valkey`

| Variable | What it does | Unless you set it |
| --- | --- | --- |
| `VALKEY_MAXMEMORY_POLICY` | What is evicted when the cache is full: `allkeys-lru` suits a cache, `noeviction` a store that must not lose keys. | `allkeys-lru` |
| `VALKEY_DATABASES` | How many numbered databases exist. | `16` |

### [Vinyl Cache (Varnish)](stack-vinyl.md) — `vinyl`

| Variable | What it does | Unless you set it |
| --- | --- | --- |
| `VARNISHD_PARAM_DEFAULT_TTL` | How long, in seconds, a page is cached when the application sends no `Cache-Control: max-age` of its own. | `120.000` |
| `VARNISH_BACKEND_GRACE` | How long an expired page may still be served while a fresh copy is fetched in the background, such as `10m`. | `2m` |
| `VARNISH_CACHE_PER_COUNTRY` | Keep a separate cached copy per visitor country, for sites that vary content by country. Set to `1`. | the image's |
| `VARNISH_MOBILE_SEPARATE_CASH` | Keep a separate cached copy for mobile browsers, for sites that render differently on phones. Set to `1`. | the image's |
| `VARNISH_KEEP_ALL_PARAMS` | Keep marketing query parameters such as `utm_source` in the cache key instead of stripping them. Set to `1`. | the image's |

"Unless you set it" is the platform's value where it sets one. Otherwise it is
the image's own default.

## What stays with the platform

Every service takes far more settings than are listed here. The others are
held back on purpose. Setting one of them would not tune the service. It would
change what the service is, or what it may reach. They fall into a few kinds:

- **Where things connect.** Hostnames, ports, cluster membership, which
  backend Varnish sends requests to. The platform addresses every service in
  your environment. A different value would point it somewhere else, and if
  that worked, the somewhere else would be another customer's.
- **Who and where.** Users, file ownership and paths. Your code is mounted
  read-only at a path the deploy switches atomically, and the services are
  built around that.
- **Credentials.** Database passwords, cache passwords, search admin
  passwords. Generated per environment and handed to your application in
  its [environment](variables.md). You never need to know them.
- **What keeps data safe.** How the database flushes to disk, where it
  stores files, and whether a cache persists. Your backups and restores
  depend on these, and a faster setting is usually a less durable one.
- **Logs.** Where logs go and in what format. The platform collects them
  from a known place, so you can [read them](logs.md) without changing
  your code.
- **Admin and debug endpoints.** Status pages, debuggers, profilers and
  cache purging. Each one is a door into a production site, and a door
  opened by a line in a repository is a door anyone who can push a branch
  can open.

If you need one of these changed, ask support at support@vallic.com. Tell us
what you are trying to do rather than which variable to set. That is usually
a setting the platform should own, or one this page should list.
