Skip to content

Deploying Jay

Listener Default Expose to
S3 API :9000 Whatever needs to store objects, including the public internet if that is your design
Admin API :9001 Nothing outside your network. It creates accounts and tokens
Native protocol :4444 Nothing outside your network, unless you give it TLS — the handshake carries the token secret in the clear by default

If you do not use the native protocol, disable its listener entirely by setting JAY_NATIVE_ADDR to an empty value. The startup line then reports "native":"disabled".

Jay terminates TLS itself when you give it a certificate:

Terminal window
JAY_TLS_CERT=/etc/jay/fullchain.pem
JAY_TLS_KEY=/etc/jay/privkey.pem

This covers the S3 and admin listeners. The native protocol has its own pair and is not covered by the one above:

Terminal window
JAY_NATIVE_TLS_CERT=/etc/jay/native-fullchain.pem
JAY_NATIVE_TLS_KEY=/etc/jay/native-privkey.pem

They are separate so that turning on TLS for S3 cannot silently change the native transport and break clients already speaking to it in the clear. Setting one half of either pair without the other aborts startup rather than serving in the clear. Left unset, the native listener stays plaintext and belongs on an internal network — Jay says so in a startup warning.

Enable TLS if you use mc or warp. minio-go signs plaintext uploads with SigV4’s streaming mode, which Jay refuses; over TLS the same clients upload normally. See S3 compatibility.

Jay does not trust forwarding headers by default, because a client that can forge X-Forwarded-For can evade the pre-auth rate limiter and poison your access logs.

Terminal window
JAY_TRUST_PROXY_HEADERS=true

With this on, X-Forwarded-For and X-Real-IP are honoured only when the direct TCP peer is loopback or RFC1918. A request arriving straight from the internet carrying the header is still attributed to its real address.

Make sure your proxy does not buffer request bodies to disk if you upload large objects, and that its own body-size limit is at or above JAY_MAX_OBJECT_SIZE (5 GiB by default).

The whole state of the server is JAY_DATA_DIR. That does not mean copying that directory backs Jay up — meta/jay.db is a live bbolt file, and a copy taken from a running server can be unrestorable. See Backup and restore.

Readiness fails when free space on that filesystem drops below JAY_MIN_FREE_BYTES (500 MiB by default), which pulls the instance out of a load balancer pool before the disk fills. A full disk corrupts bbolt; Jay would rather stop serving than get there.

Jay snapshots its bbolt metadata every hour, verifies each snapshot after writing it, keeps 24 and prunes after 7 days. A snapshot that fails verification is deleted — an unrestorable backup is worse than none, because it satisfies a retention policy while being useless.

Two things to be clear about:

  • The snapshot covers metadata only. Object bytes are not in it and Jay keeps no copy of them anywhere. Backing up buckets/ is your job, and Jay cannot be restored without it. A PUT over an existing key also replaces the file, and the old bytes are gone — Jay has no versioning.
  • JAY_METADATA_BACKUP_DIR defaults to <JAY_DATA_DIR>/backups, which is the same disk. For real disaster recovery, point it at a separate volume. Jay warns at startup while it is not, and reports it in the durability block of /health/ready.

The full procedure — what to back up, in what order to restore it, and how to verify the result — is in Backup and restore.

Both are on the admin port and need no authentication:

GET /health/live always 200 while the process is up
GET /health/ready 200 once startup recovery has finished

Readiness is a live check, not a one-time flag. It fails if a bbolt read transaction does not complete within its timeout, or if free space drops below the floor. If readiness starts failing, look at the disk first.

Readiness also carries a durability block on every response, stating what this instance has a recovery path for:

{
"status": "ready",
"durability": {
"metadata_backup": "hourly verified snapshot of meta/jay.db (bbolt): accounts, buckets, object records, tokens, multipart state",
"object_bytes_backup": "none — object bytes under buckets/ — jay keeps no copy of them; back that directory up separately",
"metadata_backup_dir": "/var/lib/jay/backups",
"metadata_backup_shares_data_filesystem": true
}
}

It never changes the status code. Object bytes having no backup is a property of Jay’s design, not a fault of the instance; failing readiness over it would pull a healthy server out of the pool forever.

Jay is a single process over a single data directory. Stop it, replace the binary or image, start it. Startup recovery reconciles metadata against the files on disk before the server accepts traffic, so an unclean stop is a handled case rather than an incident.