Deployment¶
Run Sluice as a container or as a system service.
Example¶
Goal: start Sluice with TLS on the admin listener and check that it is ready.
admin:
enabled: true
listen: ":9091"
# The variable SLUICE_ADMIN__TOKEN sets the token.
tls:
certFile: "./data/tls/server.crt"
keyFile: "./data/tls/server.key"
Start it:
# Demo secrets. Do not use these values in production.
export SLUICE_ADMIN__TOKEN="demo-admin-token-change-me"
export SLUICE_AUDIT_GENESIS="deployment-demo-genesis"
exec sluice serve --config server.yaml
Check readiness:
{
"status": "ok",
"version": "<version>",
"datasources": [
{
"name": "shop",
"type": "sqlite",
"healthy": true
}
]
}
Call the admin API:
curl -s https://localhost:9091/admin/healthz \
--cacert data/tls/server.crt \
-H "Authorization: Bearer demo-admin-token-change-me"
What happened: the data source is healthy. The admin API answered over TLS, with the token.
Ports and health endpoints¶
| Port | Key | Serves |
|---|---|---|
8080 | rest.listen | Queries, health endpoints, approval links. |
9091 | admin.listen | The Admin API and /metrics, with admin.enabled: true. |
| None | mcp.listen | MCP over HTTP. Set it for mcp.transport: streamable_http. |
Keep the admin port off the public network.
| Endpoint | Listener | Answer |
|---|---|---|
GET /v1/health | REST | 200 while the process runs. |
GET /v1/ready | REST | 200, or 503 with status degraded while a data source is unhealthy. |
GET /v1/version | REST | The build. |
GET /admin/healthz | Admin | The same status, plus config_version. Needs the token. |
The REST endpoints need no login.
{
"version": "<version>",
"commit": "<commit>",
"build_time": "<time>",
"go": "<go-version>",
"parser_version": "pg_query_go/v6 proto=170007"
}
TLS¶
rest.tls, mcp.tls and admin.tls take the same keys. The lowest version is TLS 1.2.
| Key | Meaning |
|---|---|
certFile, keyFile | Certificate and key as PEM files. Both are required. Sluice does not start if it cannot read them. |
clientCA | A CA file for mutual TLS. Clients must then present a certificate that this CA signed. |
Without a tls block, a listener serves plain HTTP and logs a warning. The example creates a test certificate:
# A self-signed certificate for localhost. In production, use one from your CA.
mkdir -p data/tls
openssl req -x509 -newkey rsa:2048 -nodes -days 30 \
-subj "/CN=localhost" -addext "subjectAltName=DNS:localhost" \
-keyout data/tls/server.key -out data/tls/server.crt 2>/dev/null
Container¶
| Tag | Content |
|---|---|
ghcr.io/bino-bi/sluice:<version> | linux/amd64 and linux/arm64. Use this tag. |
<version>-amd64, <version>-arm64 | One architecture. |
latest | Published only by a stable release, never by a pre-release. |
- The tag has no leading
v: releasev0.1.0-rc.3is tag0.1.0-rc.3. - The images are signed with cosign. See Install.
- The image has no shell and runs as user
65532in/var/lib/sluice. This user must read the mounted files and write the audit log. For the compose file below, runserve.shonce, thensudo chown -R 65532:65532 datainexamples/deployment. - The image declares
8080,8081and9090. Publish the ports that you configured. docker build -t sluice:dev .builds your own image.SLUICE_IMAGE=sluice:devmakes the example compose files use it.--build-arg LIBDUCKDB_JOBS=8raises the 4 jobs of its DuckDB compile.
# Runs the released image with the files of this example.
services:
sluice:
image: ${SLUICE_IMAGE:-ghcr.io/bino-bi/sluice:0.1.0-rc.3}
ports:
- "8080:8080"
# Only this host can reach the admin API.
- "127.0.0.1:9091:9091"
environment:
SLUICE_ADMIN__TOKEN: "${SLUICE_ADMIN_TOKEN:?set SLUICE_ADMIN_TOKEN}"
SLUICE_AUDIT_GENESIS: "${SLUICE_AUDIT_GENESIS:?set SLUICE_AUDIT_GENESIS}"
volumes:
- ../server.yaml:/etc/sluice/server.yaml:ro
- ../policies.d:/etc/sluice/policies.d:ro
# User 65532 must own this directory: it reads the TLS key and writes the audit log.
- ../data:/etc/sluice/data
working_dir: /etc/sluice
command: ["serve", "--config=/etc/sluice/server.yaml"]
restart: unless-stopped
systemd¶
# Install as /etc/systemd/system/sluice.service.
[Unit]
Description=Sluice
After=network-online.target
Wants=network-online.target
[Service]
User=sluice
Group=sluice
WorkingDirectory=/var/lib/sluice
# Holds SLUICE_ADMIN__TOKEN and the secret variables. Give it mode 0600.
EnvironmentFile=/etc/sluice/sluice.env
ExecStart=/usr/local/bin/sluice serve --config /etc/sluice/server.yaml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
[Install]
WantedBy=multi-user.target
Signals and exit status¶
| Signal | Effect |
|---|---|
SIGTERM, SIGINT | Sluice stops. REST requests get 10 seconds to finish, admin requests 5. |
SIGHUP | Sluice reloads the policy directory. |
Exit status 1 means a start error, 2 a listener error.
DuckDB extensions¶
Most data source types need a DuckDB extension. Some builds download it to ~/.duckdb/extensions at first use.
| Build | Included | Downloads for |
|---|---|---|
| Container images | All extensions | Nothing |
linux_amd64 release, make build-static | httpfs, excel, sqlite_scanner | postgres, mysql, motherduck |
linux_arm64 and darwin releases, make build | Nothing | Every type except duckdb_file |
make build-static first runs make libduckdb, a slow compile. scripts/fetch-duckdb-extensions.sh fills the directory ahead of time.