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

# MySQL

> Run managed MySQL with a persistent disk, generated credentials and scheduled backups.

MySQL runs as an ordinary service inside your project. Siblings reach it at its service
name, and it has no public endpoint unless you enable external access.

Every MySQL client and ORM works unchanged. The wire protocol, the `mysql://` URL and the
`SHOW` and `GRANT` surface are stock MySQL.

## Creating an instance

**Console**

Select **New → Database**, then **MySQL**. The form asks for:

* **Name**, the service name siblings connect to.
* **Version**, 8.4 or 8.0.
* **Database**, the schema created on first boot. It defaults to a randomised name
  derived from the service rather than a guessable `mysql`.
* **Compute**. A database reserves its full CPU allocation and does not burst.
* **Disk**, mounted at `/var/lib/mysql`.

**slipway.yaml**

```yaml slipway.yaml theme={"system"}
version: 1

databases:
  - name: db
    engine: mysql
    version: "8.4"
    diskSizeGB: 20
    cpu: "1"
    memory: 2GB

services:
  - name: api
    runtime: docker
    env:
      DATABASE_URL: ${service.db.url}
```

| Engine | `engine:` | Versions | Default | Port | Data path | Memory floor |
| - | - | - | - | - | - | - |
| Postgres | `postgres` | 18, 17, 16, 15 | 18 | 5432 | `/var/lib/postgresql` | 512 MiB |
| Redis | `redis` | 8 | 8 | 6379 | `/data` | 512 MiB |
| MySQL | `mysql` | 8.4, 8.0 | 8.4 | 3306 | `/var/lib/mysql` | 1 GiB |
| ClickHouse | `clickhouse` | 25.3, 24.8 | 25.3 | 8123 HTTP, 9000 native | `/var/lib/clickhouse` | 2 GiB |

`latest` is not a valid version. A managed database has to be reproducible, so the tag is
always pinned.

The memory floor is the smallest allocation the engine runs on. If you configure less,
the deploy fails rather than silently raising it.

MySQL needs at least 1 GiB. A smaller allocation fails validation rather than being
silently raised.

### 8.4 and older clients

MySQL 8.4 no longer enables the legacy `mysql_native_password` plugin by default. Accounts
use `caching_sha2_password`, which every current client speaks. Very old drivers (PHP's
`mysql_*` functions, MySQL Connector/J before 8.0, some 2018-era Go and Python drivers) may
fail to authenticate. Choose 8.0 if you have one you cannot upgrade.

## Authentication

Each instance is provisioned with a generated password for the `root` account, stored as
the project secret `<SERVICE>_PASSWORD`.

Additional users are managed on the service's **Credentials** tab. Each is created live
with a generated password and full privileges on the instance's database, and the password
is stored as a revealable project secret. Users live in the `mysql` system schema on the
disk, so they survive restarts.

`root` cannot be dropped from that tab. It is the account slipway manages the instance
with.

## Connecting

```yaml slipway.yaml theme={"system"}
services:
  - name: api
    env:
      DATABASE_URL: ${service.db.url}
```

`${service.db.url}` resolves to `mysql://root:<password>@db:3306/<database>`.

Available fields: `url`, `user`, `password`, `host`, `port`, `database`, and `external_url`
when external access is on.

## External access

Off by default. An admin can expose the instance from the service's Connection card. TLS is
required and the password still authenticates end to end.

### Your username carries the endpoint

MySQL clients send no TLS SNI in any ssl-mode. Postgres and Redis endpoints are told apart
by the server name in the TLS handshake, so for MySQL the hostname cannot say which
database you want. The username does instead.

```
host:     <service>-<id8>-my.db.<zone>
port:     3306
username: <user>.<service>-<id8>-my
```

An instance whose endpoint is `orders-1a2b3c4d-my` is reached by the `root` account as
`root.orders-1a2b3c4d-my`. Everything after the last dot is the endpoint; everything before
it is the account. Your account name may itself contain dots, and only the last one is the
separator. Azure Database for MySQL uses the same shape for the same reason.

The Connection card shows the full username with a copy button, which is what to paste into
a GUI client's Username field.

```bash theme={"system"}
mysql -h orders-1a2b3c4d-my.db.<zone> -P 3306 \
      -u 'root.orders-1a2b3c4d-my' -p --ssl-mode=REQUIRED
```

A Go DSN, where TLS is a parameter rather than a flag:

```
root.orders-1a2b3c4d-my:<password>@tcp(orders-1a2b3c4d-my.db.<zone>:3306)/<database>?tls=true
```

### If the gateway refuses

Two refusals come from the gateway itself, and each names its fix in the error text.

| Message | Fix |
| - | - |
| `Your MySQL username must name the database endpoint` | You connected as a bare account name. Add the `.<endpoint>` suffix. |
| `This connection must use TLS` | The gateway will not proxy a plaintext connection, because the password would cross the internet in the clear. Set `--ssl-mode=REQUIRED`, `tls=true`, or your driver's equivalent. |

## Backups

The disk is backed up on a schedule and can be restored in place from the service's
**Disks** tab. Retention is measured in days and depends on your plan. See
[Disks](/data/disks).

A restore returns the whole instance to that point. For a logical export, run `mysqldump`
from a sibling service or over external access.

## Metrics

The service overview shows CPU, memory and active connections, the live
`Threads_connected` gauge sampled every 30 seconds. The
[Metrics tab](/observability/metrics) charts those over a window you choose, plus disk
usage against the volume's size.

## Pricing

| What | Rate |
| - | - |
| CPU | \$8.76 per vCPU per month |
| Memory | \$4.38 per GiB per month |
| Disk | \$0.25 per GiB per month |
| Volume backups | \$0.05 per GiB per month |
| Object storage | \$0.05 per GiB per month |
| Egress beyond the plan allowance | \$0.01 per GB |

Compute is metered by the second against the allocation a service is configured with, so
a service that sleeps costs nothing while it is asleep. Disk, backups and object storage
are metered on what is provisioned or held, whether or not the service is running.

MySQL is priced on the allocation you configure, with CPU reserved rather than burstable.
The disk is billed on its provisioned size.

## FAQ

**Can I upgrade 8.0 to 8.4 in place?**

**No.** Change the version and slipway starts a new instance on the new image. Move the
data with `mysqldump` and restore into the new instance.

**Is `latest` allowed as a version?**

**No.** A managed database has to be reproducible, so the tag is always pinned.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.