# Deploying to production

> Deploy a Core to production.

This page walks you through deploying a Core for production use, which requires a few additional steps compared to development use.

## License keys

A standalone Core needs a license key. [Contact us](https://www.synnaxlabs.com/#contact) to get one, then provide it on startup. The easiest way to do this is using the `--license-key` flag when starting the Core. Here’s an example:

```sh
synnax start --listen=localhost:9090 --license-key=000000-00000000-0000000000
```

In production, we recommend using an environment variable or configuration file to store your license key. This is more secure than passing the key as a command line argument. The following example shows how to set the license key using an environment variable:

```sh
export SYNNAX_LICENSE_KEY=000000-00000000-0000000000
synnax start --listen=localhost:9090
```

To see the full list of command line options, environment variables, and configuration file parameters, see the [CLI reference](https://docs.synnaxlabs.com/reference/core/cli-reference).

## Configuring TLS

We recommend using TLS for securing all communications with your Core.

### TLS certificate options

There are four important command line options for configuring TLS:

| Option        | Description                                                                                                                                                                                                                                                                  | Default                     |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
| `--certs-dir` | The path to a directory containing the necessary certificates for the Core to use.                                                                                                                                                                                           | `"/usr/local/synnax/certs"` |
| `--ca-cert`   | The path relative to \`--certs-dir\` to the certificate authority (CA) certificate. This is only required if you're using a self-signed certificate. Certificates generated by public CA's like Let's Encrypt are already trusted, so you don't need to specify this option. | `"ca.crt"`                  |
| `--node-cert` | The path relative to \`--certs-dir\` to the node's public certificate file.                                                                                                                                                                                                  | `"node.crt"`                |
| `--node-key`  | The path relative to \`--certs-dir\` to the node's private key file.                                                                                                                                                                                                         | `"node.key"`                |

## Starting the Core with TLS

When starting the Core with TLS, you’ll need to specify the correct options and start the Core with the correct hostname in the `--listen` option. **The hostname must match the hostname in the node certificate**. The Core checks this at startup and refuses to start when the certificate does not cover the address it advertises. Here’s an example for a let’s encrypt certificate:

```sh
synnax start \
  --listen=synnax.example.com:9090 \
  --mem \
  --certs-dir=/etc/letsencrypt/live/synnax.example.com/ \
  --node-cert=fullchain.pem \
  --node-key=privkey.pem \
```

## Generating certificates

### Using Synnax’s auto-cert feature

The easiest way to start a secure Core using self-signed certificates is to use the `--auto-cert` command line flag when starting the Core. This will automatically generate all of the relevant certificates and keys for you:

```sh
synnax start --listen=localhost:9090 --auto-cert
```

Please note that the `--auto-cert` option will require you to install the generated CA certificate on your client machine in order to trust the self-signed certificate authority. We have a guide on [deploying Synnax with self-signed certificates](https://docs.synnaxlabs.com/reference/core/installing-certificates).

`--auto-cert` also keeps the certificate current. If you change the address the Core listens on, the next start issues a replacement pair that covers it and logs the addresses the old one missed. A certificate that already covers every address is left untouched. Without `--auto-cert` the Core never writes these files. `synnax cert node` refuses to overwrite an existing pair, so delete `node.crt` and `node.key` before you reissue them.

### Using Let’s Encrypt with Certbot

The easiest way to get started with a trusted certificate is to use [Certbot](https://certbot.eff.org/) to generate a certificate for your domain. Please note that this requires your domain to be publicly accessible and have a valid DNS record. Once you have a certificate, you can use the `--certs-dir` option to specify the directory containing the certificate and key files. See the example above for a reference on how to do this.

#### Important caveats when using Docker

Certbot generates symlinked certificates and keys in `/etc/letsencrypt/live/yourdomain.com/`. When using Docker, you’ll need to mount the entire `/etc/letsencrypt` directory into the container, as the `live` directory contains symlinks to the actual certificate and key files. Here’s an example of how to do this:

```bash
docker run -v /etc/letsencrypt:/usr/local/synnax/certs \
  -p 9090:9090 \
  synnaxlabs/synnax \
  -l localhost:9090 \
  -vm \
  --certs-dir=/usr/local/synnax/certs/live/demo.synnaxlabs.com \
  --node-cert=fullchain.pem \
  --node-key=privkey.pem
```

### Using your own certificates

When using your own certificates, you’ll need to specify the `--certs-dir` option and provide the necessary certificates and keys in that directory.

## Serving multiple listeners

By default, a Core binds a single address and presents a single certificate on it. A Core can instead bind several **listeners**, each with its own address and its own certificate. Every listener serves the full API; they differ only in where they bind and what certificate they present.

Use this to route different traffic over different networks. For example, hardware drivers reach the Core at a corporate address with your production PKI certificate, while operators reach the same Core at a Tailscale `ts.net` address with a Tailscale-issued certificate, so Tailscale can enforce per-user network access.

Multiple listeners are configured through the `listen` key in a [configuration file](https://docs.synnaxlabs.com/reference/core/cli-reference#configuration-files). In this form, `listen` is a list of listener objects instead of a single address:

```yaml
listen:
  - address: core01.example.com:9090
    cert:
      source: file
      cert: /usr/local/synnax/certs/driver.crt
      key: /usr/local/synnax/certs/driver.key
    advertise: true
  - address: node01.example-tailnet.ts.net:9091
    cert:
      source: tailscale
    name: console

peers:
  - core02.example.com:9090
  - core03.example.com:9090
```

Drivers reach `:9090` with the production certificate, operators reach `:9091` over Tailscale with the auto-provisioned `ts.net` certificate, and peers gossip over the advertised `core01.example.com:9090` address.

### Certificate sources

Each listener draws its certificate from a **source**, selected by `cert.source`. Different listeners can use different sources.

| Source      | Description                                                                                                                                                                                                                                                                                            |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `file`      | Serves a PEM certificate and key from disk. Requires `cert` and `key` paths. Reloads automatically when the files change, so certificate rotation needs no restart.                                                                                                                                    |
| `auto`      | Self-signs a certificate from the Core's built-in CA, deriving its SANs from the listener address. Do not set `cert` or `key`. Intended for development and self-contained on-prem deployments.                                                                                                        |
| `tailscale` | Provisions and renews a `ts.net` certificate through the local `tailscaled` daemon. Requires a running `tailscaled` with HTTPS enabled on the tailnet. When the Core runs as a non-root user, that user must be the Tailscale operator: `tailscale set --operator=<user>`. Do not set `cert` or `key`. |

### The advertised listener

Peers dial one agreed address to join the cluster, so exactly one listener is **advertised**. Set `advertise: true` on that listener. If no listener sets it, the first listener is advertised. Setting it on more than one listener is a startup error.

Peers are not the only clients that dial this address. The embedded Driver dials it as well. Peers trust the cluster CA alone. The Driver also trusts the node certificate, so an externally issued node certificate satisfies it.

> The advertised listener cannot use the `tailscale` source, because a public-CA certificate cannot chain to the cluster CA. The exception is a single-node Core started with `--no-driver` and no `peers`. A Core that already joined a cluster refuses the `tailscale` source at startup even when `peers` is omitted.

### Rules

The Core enforces these at startup:

* Listener addresses must be unique.
* In secure mode, every listener must resolve a certificate source.
* A `file` source must set both `cert` and `key`; `auto` and `tailscale` must set neither.
* A `listen` list cannot be combined with the global `--auto-cert`, `--node-cert`, or `--node-key` flags. Configure each listener’s certificate in its `cert` block instead.
* The advertised listener must serve a certificate valid for the advertised host. No client can verify one that omits it.
* With `peers` configured or persisted cluster members, the advertised certificate must also chain to the cluster CA.
* With the embedded Driver enabled and no peers, the advertised certificate must chain to one of the Core’s trust anchors: the cluster CA or the node certificate.

> A `listen` list rejects `--auto-cert`, but the Core still loads `node.crt` and `node.key` at startup, whatever sources the listeners use. Create them first with `synnax cert ca` and `synnax cert node`.

`--insecure` remains a whole-node switch: it disables TLS on every listener and ignores all certificate sources.
