Goal
Register a Linux server you own as an Appaloft deployment target, and confirm Appaloft can reach it securely over SSH.
When to use this task
- Onboarding a new server for the first time.
- Migrating to a new machine, or temporarily troubleshooting a server’s connectivity.
Prerequisites
- A Linux server reachable over SSH, that you have root or sudo access to.
- An SSH key (a dedicated deploy key is recommended, rather than your personal login key).
Inputs and defaults
| Input | Description | Default |
|---|---|---|
host | An explicit hostname, IPv4, or IPv6 address | None, required; CIDR, URLs, and host:port forms are not accepted |
port | SSH port | 22 |
user | The system user used to connect | None, required |
| Credential | SSH key path / a saved credential / one-time input | None, required |
| Display name / label | Helps you tell servers apart in the list | Optional |
| Workload roles | Placement intent for new workload categories | Empty: general purpose for all workload types |
Server registration input should not include resource, environment, or domain-related fields — those are independent configuration layers.
Workload roles
Workload roles declare which categories of new workload a server is intended to accept. They are placement intent, not proof that the server is healthy, ready, or technically capable. Choose any combination of these canonical values:
deployment-runtime: may accept new application runtime placement.artifact-builder: records intent to accept artifact build execution when a governed builder-placement path exists. It does not mean remote build execution is currently available or ready.sandbox-worker: may accept new Sandbox placement, but isolation and provider capability checks still apply independently.
No roles means General purpose (all workload types). API and structured CLI output preserve this as workloadRoles: []; list and show output do not hide the empty array or interpret it as incapable.
Role changes affect new placement only. They do not drain, stop, move, cancel, or reinterpret existing workloads or historical placement records. Lifecycle, connectivity, readiness, capacity, tenant, and provider checks remain independent. Unknown or duplicate roles are rejected.
Register a classified server with repeatable --workload-role options:
appaloft server register \
--name primary \
--host 203.0.113.10 \
--workload-role deployment-runtime \
--workload-role artifact-builderReplace the complete role set after registration:
appaloft server configure-workload-roles srv_primary \
--workload-role deployment-runtime \
--workload-role sandbox-workerRestore general-purpose placement by passing no role options:
appaloft server configure-workload-roles srv_primaryUse appaloft server list or appaloft server show <serverId> to read the normalized role array. If a deployment is rejected because the selected server does not allow deployment-runtime, select an eligible server, configure that role, or restore the empty general-purpose set. Changing roles does not repair runtime readiness or add remote build or Sandbox capability.
Web steps
- Go to Servers, click Register server.
- Fill in the host, port, user, SSH credential, and optional workload roles.
- Submit the form, then inspect the normalized role set and connectivity result.
CLI steps
Prefer the single task command when onboarding the local machine or an SSH server:
# Local Mac/Linux direct target
appaloft server enroll --local --name "Local machine"
# Enroll a VPS through the local SSH agent
appaloft server enroll ssh://[email protected]:22 --name primary
# Or read a private key from a local file; key bytes never enter argv, output, or Server readback
appaloft server enroll ssh://[email protected] \
--private-key-file ~/.ssh/appaloft_deploy
# A stored SSH credential can also be reused
appaloft server enroll ssh://[email protected] \
--credential-id sshcred_primaryenroll reuses registration, credential attachment, connectivity diagnostics, runtime preparation,
and Server show in order. After registration it first prints a server-enrollment-checkpoint/v1
value with the serverId. It prints server-enrollment/v1 only after runtime preparation reports
ready and real readback completes. A later failure does not delete the Server; use that id with
server credential, server doctor, server runtime prepare, or server show to repair and retry.
The SSH target accepts only ssh://user@host[:port]. Passwords, paths, queries, fragments, and
other schemes are rejected before registration.
The granular commands below remain available for scripts and manual repair:
# Register an IPv4 server
appaloft server register \
--name primary \
--host 203.0.113.10 \
--port 22 \
--provider generic-ssh \
--target-kind single-server
# Register an IPv6-only server
appaloft server register \
--name ipv6-primary \
--host 2001:db8::1 \
--port 22 \
--provider generic-ssh
# Manually rerun the connectivity test
appaloft server test srv_primary
# View server details (does not trigger a new connectivity test)
appaloft server show srv_primary
# Rename (only affects the display name)
appaloft server rename srv_primary --name "Primary SSH server"# Replace the complete workload role set
appaloft server configure-workload-roles srv_primary \
--workload-role deployment-runtime \
--workload-role sandbox-worker
# Restore general-purpose placement
appaloft server configure-workload-roles srv_primaryCluster targets like Docker Swarm need to explicitly declare their shape:
appaloft server register \
--name swarm-cluster \
--host 203.0.113.20 \
--provider docker-swarm \
--target-kind orchestrator-clusterExisting Kubernetes clusters
A Kubernetes cluster is also an orchestrator-cluster, while its connection information is owned
separately by a Runtime Target Profile. The profile persists URI-shaped opaque references only. It
does not accept kubeconfig YAML, tokens, certificates, namespaces, or manifests as registration or
deployment input.
Register the cluster target, then configure its connection reference:
appaloft server register \
--name production-cluster \
--host kubernetes.invalid \
--port 6443 \
--provider kubernetes \
--target-kind orchestrator-cluster
appaloft server configure-runtime-target-profile srv_cluster \
--connection-reference file:///absolute/path/to/kubeconfig \
--routing-policy-reference builtin://kubernetes/ingress-controller/traefik-k3sThe public local composition resolves absolute file:// kubeconfig references by default and does
not copy file contents into Server readback. To keep credentials separately, also configure a
--credential-reference secret://... and inject a credential-aware adapter that can resolve it.
Without that adapter, readiness and deployment fail closed instead of falling back to local Docker
or another target.
Routed workloads also require an explicit routing policy reference. The public built-in reference
above allows ingress only from Pods in kube-system whose exact selector is
app.kubernetes.io/name=traefik; it does not allow a wildcard namespace, Pod selector, or CIDR.
Clusters with another ingress topology must inject a resolver for their own opaque policy
reference. An absent, unknown, or invalid policy remains blocked before apply.
Run the non-mutating readiness inspection after configuration:
appaloft server readiness srv_clusterThe response always normalizes API reachability, version, authorization, namespace isolation, routing, and storage checks without returning Kubernetes API DTOs or raw credentials. The Web Server detail page exposes the same profile dialog and readiness readback for cluster targets; HTTP/API, SDK, and MCP consume the same operation schemas.
When a Resource mounts a named StorageVolume, the Kubernetes backend creates a StatefulSet and
PVC under a stable storage scope. A new Deployment receipt reuses the same workload and PVC, while
cleanup of the old receipt leaves durable storage intact. Existing StorageVolume backup/restore
operations can back up that PVC and restore it to a new independent PVC by default.
A helm-chart source enters a typed Helm lifecycle through the same deployment operation rather
than accepting raw manifests. Chart reference, version, values secret references, hook policy, and
timeout belong to the source binding. Execution renders a redacted review, performs an atomic
upgrade/wait, and must prove the previous manifest after a failed upgrade. Cleanup uses the exact
release identity and a foreground uninstall. For example:
appaloft deploy oci://registry.example.com/charts/storefront \
--method helm \
--helm-chart-version 1.7.3 \
--helm-values-secret-ref secret://helm/storefront/production \
--helm-hook-policy bounded \
--helm-timeout-seconds 420The same profile can be committed without raw values:
source:
type: helm
chart: oci://registry.example.com/charts/storefront
version: 1.7.3
valuesSecretReferences:
- secret://helm/storefront/production
hookPolicy: bounded
timeoutSeconds: 420
runtime:
strategy: helmThe public Helm lifecycle accepts absolute file://, OCI, or HTTPS chart references. Its default
local values resolver accepts absolute file:// references; Cloud/Enterprise can inject a
credential-aware values resolver for managed secret references.
Outbound Server Worker
When a Mac/VPS cannot or should not expose inbound SSH, the device can initiate one outbound mTLS Worker connection. It attaches to an existing public Server. Worker connection, generation, lease, and certificate state are separate readback; revoking a Worker never deletes or renames the Server.
With an authenticated Cloud/self-hosted profile from appaloft login:
appaloft server worker enroll --server srv_primary --name "My Mac"
appaloft server worker runenroll uses the active authenticated profile to issue a short-lived one-time token, generates the
private key/CSR on the device, and exchanges it for a short-lived mTLS certificate. The private key
never leaves the device, and the token never enters argv, output, long-lived credentials, or Server
readback. A self-hosted flow with an administrator-transferred token can still use stdin:
printf '%s' "$ONE_TIME_TOKEN" | appaloft server worker enroll \
--server srv_primary --name "Build VPS" --token-stdinserver worker run keeps the outbound connection in the foreground so the device’s own service
manager can supervise it. Docker/container execution is the default. If the device owner explicitly
allows Appaloft to run Workspace/Dev host commands on that machine, set this only in the local Worker
environment:
APPALOFT_SERVER_WORKER_ALLOW_HOST_SHELL=true appaloft server worker runThis opt-in is device-local and cannot be enabled remotely by Cloud. Use
APPALOFT_SERVER_WORKER_ROOTS to further restrict accessible paths to owned roots separated by the
platform path delimiter. Port forwarding also requires a short-lived single-target capability; the
relay is not a general VPN.
appaloft server worker status --json
appaloft server worker revokerevoke closes the current connection, fences stale generations, and removes the local Worker
credential. The Server and persistent Workspace data keep their own lifecycle. Once connected, run
appaloft dev . --server srv_primary; see Local Development Sessions.
Expected output and status
The connectivity test checks: whether DNS/IP and the port are reachable, whether SSH credentials work, whether the target user has the permissions needed to deploy, whether the base runtime environment meets requirements, and where necessary returns proxy- or Docker-related diagnostics. The result distinguishes hostname resolution failures, network/host unreachable, authentication failures, and host-key verification failures instead of a single generic error.
Verification
- The command or Web UI shows the connectivity test passing.
- Run
appaloft server show srv_primaryand confirm status, provider, andworkloadRolesmatch what you expect.
Rollback / recovery
| Symptom | Recovery |
|---|---|
| Connection timeout | Check host, port, firewall, and network path |
| Authentication failure | Check the SSH key, user, and authorized_keys on the server |
| Insufficient permissions | Confirm the target user can execute the commands deployment requires |
| Missing runtime environment | Install or switch to a supported provider/runtime per the diagnostics |
After the connectivity test passes, continue on to Managing SSH Credentials and Proxy Readiness And Terminal Sessions.
When you no longer need a server, deactivate it before deleting it:
appaloft server deactivate srv_primary
appaloft server delete-check srv_primary
appaloft server delete srv_primary --confirm srv_primaryThe pre-delete safety check blocks deleting a server that’s still active, or still has deployment history, resources, domains, certificates, or retained audit records; deletion itself doesn’t automatically clean these up either.