Native TLS Encryption for bunqueue TCP & HTTP
TLS without the reverse proxy.
Point bunqueue at a certificate and key, and all traffic between clients and the server is encrypted. No nginx or Caddy in front, one cert pair covers both the TCP and HTTP ports.
TLS (the encryption behind https://) is opt-in: without a cert and key the server runs in plaintext, exactly as before. Turn it on whenever clients connect over a network you don’t fully trust.
Enable TLS on the server
Section titled “Enable TLS on the server”# CLI flagsbunqueue start --tls-cert ./cert.pem --tls-key ./key.pem
# Or environment variablesTLS_CERT_FILE=./cert.pem TLS_KEY_FILE=./key.pem bunqueue startOr in bunqueue.config.ts:
import { defineConfig } from 'bunqueue';
export default defineConfig({ server: { tlsCertFile: './cert.pem', tlsKeyFile: './key.pem', },});One cert pair covers both servers: TCP (:6789) and HTTP/WebSocket/SSE (:6790, which becomes https:// / wss://).
The server fails fast at startup if the cert or key file is missing, or if only one of the two is set. It never silently falls back to plaintext.
Connect a client
Section titled “Connect a client”import { Queue, Worker } from 'bunqueue/client';
// Public CA (Let's Encrypt etc.): verify with system CAsconst queue = new Queue('jobs', { connection: { host: 'queue.example.com', port: 6789, tls: true },});
// Private CA or self-signed: trust a specific CA fileconst queue2 = new Queue('jobs', { connection: { host: '10.0.0.5', port: 6789, tls: { caFile: './ca.pem' } },});
// Dev only: skip verificationconst queue3 = new Queue('jobs', { connection: { host: 'localhost', port: 6789, tls: { rejectUnauthorized: false } },});import { Queue, Worker } from 'bunqueue-client';
// Public CA (Let's Encrypt etc.): verify with system CAsconst queue = new Queue('jobs', { host: 'queue.example.com', port: 6789, tls: true });
// Private CA or self-signed: trust a specific CA fileconst queue2 = new Queue('jobs', { host: '10.0.0.5', port: 6789, tls: { caFile: './ca.pem' } });
// Dev only: skip verificationconst queue3 = new Queue('jobs', { host: 'localhost', port: 6789, tls: { rejectUnauthorized: false } });from bunqueue import Queue
# Public CA (Let's Encrypt etc.): verify with system CAsqueue = Queue("jobs", host="queue.example.com", port=6789, tls=True)
# Private CA or self-signed: trust a specific CA filequeue2 = Queue("jobs", host="10.0.0.5", port=6789, tls={"ca_file": "./ca.pem"})
# Dev only: skip verification (a preconfigured ssl.SSLContext also works)queue3 = Queue("jobs", host="localhost", port=6789, tls={"verify": False})use Bunqueue\Queue;
// Public CA (Let's Encrypt etc.): verify with system CAs$queue = new Queue('jobs', ['host' => 'queue.example.com', 'port' => 6789, 'tls' => true]);
// Private CA or self-signed: trust a specific CA file$queue2 = new Queue('jobs', ['host' => '10.0.0.5', 'port' => 6789, 'tls' => ['caFile' => './ca.pem']]);
// Dev only: skip verification$queue3 = new Queue('jobs', ['host' => 'localhost', 'port' => 6789, 'tls' => ['verifyPeer' => false]]);// Public CA (Let's Encrypt etc.): verify with system CAsqueue := bunqueue.NewQueue("jobs", bunqueue.Options{ Host: "queue.example.com", Port: 6789, TLS: &bunqueue.TLSOptions{},})
// Private CA or self-signed: trust a specific CA filequeue2 := bunqueue.NewQueue("jobs", bunqueue.Options{ Host: "10.0.0.5", Port: 6789, TLS: &bunqueue.TLSOptions{CAFile: "./ca.pem"},})
// Dev only: skip verificationqueue3 := bunqueue.NewQueue("jobs", bunqueue.Options{ Host: "localhost", Port: 6789, TLS: &bunqueue.TLSOptions{InsecureSkipVerify: true},})use std::path::PathBuf;use bunqueue_client::{ConnectionOptions, Queue, TlsOptions};
// Public CA (Let's Encrypt etc.): verify with system CAslet queue = Queue::new("jobs", ConnectionOptions { host: "queue.example.com".into(), tls: Some(TlsOptions::default()), ..Default::default()});
// Private CA or self-signed: trust a specific CA filelet queue2 = Queue::new("jobs", ConnectionOptions { host: "10.0.0.5".into(), tls: Some(TlsOptions { ca_file: Some(PathBuf::from("./ca.pem")) }), ..Default::default()});Rust deliberately exposes no insecure TLS mode, verification is always on. For a self-signed cert, trust it through ca_file.
# Public CA (Let's Encrypt etc.): verify with system CAsqueue = Bunqueue.queue("jobs", host: "queue.example.com", port: 6789, tls: true)
# Private CA or self-signed: trust a specific CA filequeue2 = Bunqueue.queue("jobs", host: "10.0.0.5", port: 6789, tls: true, ca_file: "./ca.pem")
# Dev only: skip verificationqueue3 = Bunqueue.queue("jobs", host: "localhost", port: 6789, tls: true, verify: false)Workers accept the same TLS options as queues in every SDK (in the Bun client, under connection.tls). The wire protocol is unchanged, TLS only wraps the transport.
From the CLI:
bunqueue stats --host queue.example.com --tls # system CAsbunqueue stats --tls-ca ./ca.pem # custom CAbunqueue stats --tls-no-verify # self-signed, dev onlySelf-signed certificate (dev / internal networks)
Section titled “Self-signed certificate (dev / internal networks)”No public domain? Generate your own cert:
openssl req -x509 -newkey rsa:2048 -nodes -days 365 \ -keyout key.pem -out cert.pem \ -subj "/CN=localhost" \ -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"Clients then connect with the CA-file option pointing at cert.pem (caFile in TypeScript and PHP, ca_file in Python, Rust, and Elixir, CAFile in Go): the self-signed cert acts as its own CA. That keeps full verification, no verification opt-out needed.
Good to know
Section titled “Good to know”- Certificate verification is on by default in every SDK: the client rejects untrusted or mismatched server certs unless you explicitly opt out (
rejectUnauthorized: falsein TypeScript,{"verify": False}in Python,['verifyPeer' => false]in PHP,InsecureSkipVerify: truein Go,verify: falsein Elixir), encryption without authentication, dev only. Rust exposes no insecure mode at all. - TLS encrypts traffic but does not identify clients. Combine it with auth tokens for servers exposed beyond localhost.
- A TLS-enabled server only accepts TLS clients; plaintext clients fail the handshake immediately (they do not hang).
- HTTP endpoints (
/health, dashboards,/ws,/events) are served overhttps:///wss://when TLS is enabled.