Managed Deployment
Set your deployment values
Fill these once. The domains, paths, volume names, and commands below update automatically across this page.
Derived values
These same values now flow through the examples and commands below.
The hostname pattern used throughout this page is:
-
acme.db.example.com -
acme.grpc.example.com
What this deployment pattern is
This is the recommended shape when you want one managed LioranDB environment for one customer or tenant:
- public HTTPS for the HTTP API
- public TLS on
:443for gRPC - metrics reachable only inside Docker
- persistent named-volume storage
- bootstrap password loaded from a mounted file
- Caddy handling all internet-facing TLS
The three template files
The managed deployment directory contains:
README.mdcompose.example.yamlCaddyfile.example
Each file has a different job.
Caddyfile.example
The routing shape for your deployment is:
acme.db.example.com {
reverse_proxy liorandb:27018
}
acme.grpc.example.com {
reverse_proxy h2c://liorandb:27019
}
What the HTTP block does
acme.db.example.com {
reverse_proxy liorandb:27018
}
This means:
- Caddy listens for public requests sent to
acme.db.example.com - Caddy terminates public TLS for that hostname
- Caddy forwards internal traffic to
liorandb:27018
This is the public HTTP entrypoint for:
/live/ready/v1- driver HTTP requests
- admin HTTP APIs
What the gRPC block does
acme.grpc.example.com {
reverse_proxy h2c://liorandb:27019
}
This means:
- Caddy listens for public gRPC traffic on
acme.grpc.example.com - Caddy terminates public TLS on port
443 - Caddy forwards internal traffic as cleartext HTTP/2 to
liorandb:27019
h2c is important here because the upstream hop is private Docker-network
traffic, not public TLS.
Why this split matters
This pattern gives you:
- one public hostname for the HTTP-facing API
- one public hostname for gRPC
- one internal HTTP bind
- one internal gRPC bind
That matches the intended driver discovery flow cleanly.
compose.example.yaml
The Compose template creates two services:
liorandbcaddy
The liorandb service
Core shape:
image: liorandb/liorandb:pre-alpha
container_name: liorandb-acme
restart: unless-stopped
cpus: 2.0
mem_limit: 3.5g
This gives you:
- the published server image
- a readable per-client container name
- automatic restarts
- basic CPU and memory constraints
Why there is no command: override
The managed template intentionally keeps the image CMD unchanged.
That means the image still boots with its baked-in server command and existing startup arguments. The deployment template only changes:
- environment variables
- reverse proxying
- persistence
- networking
Environment variables
This is the managed shape:
LIORANDB_TLS_MODE=insecure
LIORANDB_GRPC_BIND_ADDR=0.0.0.0:27019
LIORANDB_METRICS_ADDR=0.0.0.0:27201
LIORANDB_ADVERTISED_HTTP_ADDR=acme.db.example.com:443
LIORANDB_ADVERTISED_GRPC_ADDR=acme.grpc.example.com:443
LIORANDB_BOOTSTRAP_PASSWORD_FILE=/run/liorandb/bootstrap-password.txt
What each variable does
LIORANDB_TLS_MODE=insecure
- public TLS is terminated by Caddy
- the internal Docker hop remains plaintext
- this is acceptable because that hop is private to Docker
LIORANDB_GRPC_BIND_ADDR=0.0.0.0:27019
- LioranDB listens internally for gRPC on
27019
LIORANDB_METRICS_ADDR=0.0.0.0:27201
- metrics are exposed internally on
27201
`LIORANDB_ADVERTISED_HTTP_ADDR=acme.db.example.com:443`/v1should advertise the public HTTP authority clients must use
`LIORANDB_ADVERTISED_GRPC_ADDR=acme.grpc.example.com:443`/v1should advertise the public gRPC authority clients must use
LIORANDB_BOOTSTRAP_PASSWORD_FILE=/run/liorandb/bootstrap-password.txt
- the server reads the initial admin password from the mounted file
Why expose: is used instead of ports:
The managed template should look like this:
expose:
- "27018"
- "27019"
- "27201"
That makes the ports reachable to other containers, but not publicly published on the VPS host.
This is one of the most important security properties of the deployment.
Persistence
The data volume pattern should look like:
volumes:
- liorandb-acme-data:/var/lib/liorandb/data
- ./clients/acme/bootstrap-password.txt:/run/liorandb/bootstrap-password.txt:ro
This gives you:
- persistent database data in a named Docker volume
- a read-only mounted bootstrap password file
Networks
The network pattern should look like:
networks:
- public-edge
- metrics-private
And the network definitions:
networks:
public-edge:
metrics-private:
internal: true
That means:
public-edgeis shared between Caddy and LioranDBmetrics-privateis intentionally internal-only
The caddy service
The public host port mapping should stay:
ports:
- "80:80"
- "443:443"
- "443:443/udp"
That means only Caddy is exposed publicly.
You should not publish:
270182701927201
directly on the VPS.
Fresh Ubuntu VPS guide
This flow assumes:
- today is Friday, August 14, 2026
- Docker is already installed
- Docker commands require
sudo
1. Point DNS first
Create two A records pointing at your VPS public IP:
acme.db.example.com -> 203.0.113.10
acme.grpc.example.com -> 203.0.113.10
If you use Cloudflare, start with DNS only while validating the stack.
2. SSH into the VPS and prepare directories
sudo mkdir -p /opt/liorandb-acme
sudo mkdir -p /opt/liorandb-acme/clients/acme
cd /opt/liorandb-acme
3. Create the bootstrap password
Generate a strong password:
openssl rand -base64 32 | sudo tee /opt/liorandb-acme/clients/acme/bootstrap-password.txt
Lock it down:
sudo chmod 600 /opt/liorandb-acme/clients/acme/bootstrap-password.txt
View it once:
sudo cat /opt/liorandb-acme/clients/acme/bootstrap-password.txt
Save that password somewhere secure. This file is expected by the deployment template and is mounted read-only into the container.
4. Create the Caddyfile
sudo nano /opt/liorandb-acme/Caddyfile.example
Paste:
acme.db.example.com {
reverse_proxy liorandb:27018
}
acme.grpc.example.com {
reverse_proxy h2c://liorandb:27019
}
Save with:
Ctrl+OEnterCtrl+X
5. Create the Compose file
sudo nano /opt/liorandb-acme/compose.yaml
Paste:
services:
liorandb:
image: liorandb/liorandb:pre-alpha
container_name: liorandb-acme
restart: unless-stopped
cpus: 2.0
mem_limit: 3.5g
environment:
LIORANDB_TLS_MODE: insecure
LIORANDB_GRPC_BIND_ADDR: 0.0.0.0:27019
LIORANDB_METRICS_ADDR: 0.0.0.0:27201
LIORANDB_ADVERTISED_HTTP_ADDR: acme.db.example.com:443
LIORANDB_ADVERTISED_GRPC_ADDR: acme.grpc.example.com:443
LIORANDB_BOOTSTRAP_PASSWORD_FILE: /run/liorandb/bootstrap-password.txt
expose:
- "27018"
- "27019"
- "27201"
volumes:
- liorandb-acme-data:/var/lib/liorandb/data
- ./clients/acme/bootstrap-password.txt:/run/liorandb/bootstrap-password.txt:ro
networks:
- public-edge
- metrics-private
caddy:
image: caddy:2
container_name: caddy-acme
restart: unless-stopped
depends_on:
- liorandb
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile.example:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
networks:
- public-edge
volumes:
liorandb-acme-data:
caddy-data:
caddy-config:
networks:
public-edge:
metrics-private:
internal: true
6. Validate Compose before starting anything
cd /opt/liorandb-acme
sudo docker compose -f compose.yaml config
If that prints normalized configuration without errors, the file structure and YAML are valid.
7. Pull images
sudo docker compose -f compose.yaml pull
sudo docker images
You should pull:
liorandb/liorandb:pre-alphacaddy:2
8. Configure the firewall
Keep SSH open:
sudo ufw allow OpenSSH
Allow only public web/TLS traffic:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw enable
sudo ufw status
Do not open:
270182701927201
Those should remain Docker-private.
9. Start the deployment
cd /opt/liorandb-acme
sudo docker compose -f compose.yaml up -d
sudo docker compose -f compose.yaml ps
You want both containers to show as up:
-
liorandb-acme -
caddy-acme
10. Inspect logs immediately
LioranDB logs:
sudo docker logs -f liorandb-acme
You should eventually see HTTP, gRPC, and metrics listeners.
Caddy logs:
sudo docker logs -f caddy-acme
You want successful certificate acquisition for:
-
acme.db.example.com -
acme.grpc.example.com
11. Check public listening ports
sudo ss -tulpn
You should see public listeners roughly like:
0.0.0.0:80
0.0.0.0:443
You should not see:
0.0.0.0:27018
0.0.0.0:27019
0.0.0.0:27201
That is the key security check.
12. Test the public HTTP side
curl -v https://acme.db.example.com/live
curl -v https://acme.db.example.com/v1
The important /v1 response should advertise:
{
"http_addr": "acme.db.example.com:443",
"grpc_addr": "acme.grpc.example.com:443"
}
:::danger Stop here if discovery is wrong
If /v1 still reports 0.0.0.0:27018 or 0.0.0.0:27019, then the currently
published liorandb/liorandb:pre-alpha image likely predates the advertised
address changes. In that case, the template is right, but the image must be
rebuilt and republished before this managed flow is safe to rely on.
:::
13. Test private metrics
List Docker networks:
sudo docker network ls
Compose will usually create something like:
liorandb-acme_metrics-privateThen test metrics from a temporary container:
sudo docker run --rm \
--network liorandb-acme_metrics-private \
curlimages/curl \
http://liorandb:27201/metrics
You should get Prometheus-format output.
14. Build the final driver URI
Your managed connection string becomes:
liorandb://admin:<your-encoded-password>@acme.db.example.com:443/default
If the password contains characters like:
@#!/:%
URL-encode it first.
Examples:
@->%40!->%21#->%23
Expected discovery flow:
liorandb://admin:<your-encoded-password>@acme.db.example.com:443/default
-> HTTPS to acme.db.example.com:443
-> GET /v1
-> grpc_addr = acme.grpc.example.com:443
-> TLS gRPC through Caddy when needed
15. Verify persistence before calling it done
After inserting test data, restart the database container:
sudo docker restart liorandb-acme
Verify the data is still there.
Then test a fuller cycle:
cd /opt/liorandb-acme
sudo docker compose -f compose.yaml down
sudo docker volume ls | grep liorandb
sudo docker compose -f compose.yaml up -d
Your data should remain because the named volume is mounted at
/var/lib/liorandb/data.
Do not run:
sudo docker compose down -v
unless you intentionally want to delete the database volume.
Updating LioranDB later
When a newer server image is published:
cd /opt/liorandb-acme
sudo docker compose pull liorandb
sudo docker compose up -d
The persistent data volume remains untouched.
Final topology
Internet
|
|-- https://acme.db.example.com
| |
| v
| Caddy
| |
| v
| liorandb:27018
|
'-- acme.grpc.example.com:443
|
v
Caddy
|
v
h2c://liorandb:27019
metrics :27201
locked inside Docker
data
v
liorandb-acme-data
Final preflight reminder
Before you even begin the VPS rollout, verify that the currently published
liorandb/liorandb:pre-alpha image was built after the advertised HTTP and
gRPC address changes landed. If not, the deployment files can be perfect and
the older binary can still ignore the new environment variables.