Run the container image
Smeltr publishes its image at ghcr.io/smeltr-sh/smeltr, tagged by release. For a cluster, use Kubernetes.
Pull and verify the image
The release workflow signs each image and attaches its package list (a CycloneDX SBOM). A tag can move to a newer image, so pull it, then check and run that exact digest:
docker pull ghcr.io/smeltr-sh/smeltr:<version>
image=$(docker image inspect --format '{{index .RepoDigests 0}}' ghcr.io/smeltr-sh/smeltr:<version>) Both checks fail unless that workflow, run for a release tag, made the image:
cosign verify "$image" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp '^https://github.com/smeltr-sh/smeltr/\.github/workflows/release\.yml@refs/tags/v'
cosign verify-attestation --type cyclonedx "$image" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp '^https://github.com/smeltr-sh/smeltr/\.github/workflows/release\.yml@refs/tags/v' Name the verified digest the way the commands below expect:
docker tag "$image" smeltr:<version> Why not latest
A tag names the image it was last pushed to. latest, 1 and 1.2 move to each newer release they cover, which makes them a way to find the newest
release. A container keeps the image it was created from, but a new one, such as a worker added on
another host or a new Kubernetes pod, may pull whatever the tag names at that moment. After a release
those run the new build while the rest stay on the old one.
A worker takes deployments only from a leader of its own major release: 1.2 and 1.3 work together, 2.0 does not. A worker on an older release of that major refuses a configuration that uses something newer, and keeps running what it has. A leader that starts on a newer build upgrades its data without the backup an upgrade begins with. Name the image by its digest, and change the digest only as an upgrade.
Load an image archive
A host that cannot reach ghcr.io loads the image from an archive instead. Compare the archive's
SHA-256 checksum with its line in the release's SHA256SUMS, and stop if it is
missing or differs. Then load it and check its name, tag and runtime user:
sha256sum smeltr-image.tar.gz
docker load --input smeltr-image.tar.gz
docker image inspect smeltr:<version> --format '{{.Id}} user={{.Config.User}}' Run the container
The image runs as UID/GID 1000. Prepare persistent host directories before starting it:
sudo install -d -o root -g 1000 -m 0750 /srv/smeltr
sudo install -d -o 1000 -g 1000 -m 0700 /srv/smeltr/leader-data /srv/smeltr/leader-secrets Save this leader-only configuration as container-smeltr.toml in your current directory.
HTTP listens on port 4000 inside the container; the run command below restricts the published port
to the host.
[leader]
enabled = true
[leader.http]
bind = "0.0.0.0"
port = 4000
public_origin = "http://127.0.0.1:4000"
[leader.tls]
enabled = false
[leader.storage]
data_dir = "/var/lib/smeltr"
[leader.secrets]
key = "/run/secrets/smeltr/sealing.key" For a standalone container, append [worker] with enabled = true and group = "default". Its worker state stays within the leader-data mount. Leave the
sealing key file absent on a fresh installation.
sudo install -o root -g 1000 -m 0640 ./container-smeltr.toml /srv/smeltr/smeltr.toml
docker run --name smeltr-leader \
--restart unless-stopped \
--read-only --cap-drop ALL --security-opt no-new-privileges \
-p 127.0.0.1:4000:4000 \
-e SMELTR_CONFIG_FILE=/etc/smeltr/smeltr.toml \
--mount type=bind,src=/srv/smeltr/smeltr.toml,dst=/etc/smeltr/smeltr.toml,readonly \
--mount type=bind,src=/srv/smeltr/leader-data,dst=/var/lib/smeltr \
--mount type=bind,src=/srv/smeltr/leader-secrets,dst=/run/secrets/smeltr \
smeltr:<version> Read the first-login credentials from the container output, then follow First login. The bind mounts preserve state when you replace the container. The local smeltr:<version> tag above names the verified image; use the verified digest
when deploying on other hosts. Smeltr writes only to those mounts, so the run command makes the
root filesystem read-only, drops every Linux capability, and lets no process gain privileges.
Use the HTTPS proxy setup before allowing remote browsers or workers to connect.
A remote worker cannot reach this loopback-only example.