Kamal runs from your laptop or CI runner and controls Docker on a VPS over SSH. The repeatable path is: install Kamal locally, create a Docker image and config/deploy.yml, point DNS at the server, store credentials in .kamal/secrets, run kamal setup once, then use kamal deploy for releases. Kamal Proxy keeps the old container serving while the new one passes a health check, so traffic can switch without planned downtime.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
What Kamal does on a VPS
Kamal is a deployment orchestrator for containerized applications. It does not normally run as a management service on your VPS. You invoke it on a deployment machine, and it connects to the server through SSH to build or obtain an image, start containers, configure kamal-proxy, and change which container receives requests.
As an Amazon Associate I earn from qualifying purchases.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
The deployment machine can be a developer laptop or a CI runner. It needs Ruby and network access to the VPS and to your container registry. The VPS needs a reachable SSH service, a public address, and firewall rules that allow the application traffic you intend to serve. Kamal can install Docker during the initial bootstrap when Docker is absent, but you still need an account that can perform the required remote administration.
Each release is tied to the version checked out on the deployment machine. Kamal builds that version, pushes the image to a registry, pulls it on the server, starts a new container, and asks kamal-proxy to test it. The default readiness request is GET /up; traffic moves only after that request returns HTTP 200. The old container is stopped after the switch, and unused images are pruned as part of the normal deployment workflow.
Before you run Kamal
Prepare the deployment machine
- Install a supported Ruby runtime and the RubyGems package manager. A commonly cited third-party prerequisite is Ruby 2.7 or newer, but Kamal releases can raise or change that requirement, so check the install guidance for the version you are using.
- Install Kamal with
gem install kamaland verify it withkamal version. - Install Git and make sure the working tree contains the exact commit you intend to deploy.
- Generate or select an SSH key that can log in to the VPS. Test the connection directly before troubleshooting Kamal:
ssh [email protected].
Prepare the VPS
- Use a public IPv4 address. Add an AAAA record only when IPv6 is correctly configured and reachable on the server.
- Allow SSH (normally TCP 22), HTTP (TCP 80), and HTTPS (TCP 443) through the VPS firewall and any cloud security group. Port 443 is required for the HTTP challenge used by automatic Let’s Encrypt certificates.
- Create a non-root deployment account where practical, give it the SSH key, and grant the Docker permissions that your operating system requires. Several deployment guides recommend avoiding routine root logins.
- Leave enough disk space for the current image and at least one previous image during a rolling switch. Kamal prunes old images after deployment, but pruning does not eliminate the space needed to stage a new release.
Point DNS at the server
Create an A record such as app.example.com pointing to the VPS public IPv4 address. Public DNS must resolve to this address before HTTPS can be issued. A stale record, an internal-only record, or a proxy in front of the host can make certificate issuance and health checks appear to fail even when the container is healthy.
Containerize the web app
Kamal deploys a container image, so the application needs a Dockerfile and a process that listens on the port you declare to the proxy. The following minimal example is illustrative; replace the base image, build commands, and start command with those used by your framework.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →FROM ruby:3.3-slim
WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN bundle install
COPY . .
ENV RAILS_ENV=production
EXPOSE 3000
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
Ensure the server inside the container binds to 0.0.0.0, not only to the loopback interface, and that the process responds on the same port used by app_port. Add a lightweight /up endpoint that returns 200 without depending on a slow external service. Kamal Proxy uses this endpoint to decide whether a new container is ready; an authentication redirect, a 404, or a 500 response prevents the traffic switch.
Build the image locally before the first remote deployment when possible. This catches missing files, dependency failures, and an incorrect start command before Kamal is involved:
docker build -t registry.example.com/team/myapp:local .
docker run --rm -p 3000:3000 registry.example.com/team/myapp:local
Create the Kamal configuration
Run kamal init in the application repository. It creates config/deploy.yml and a .kamal/secrets file. Keep the secrets file out of version control; commit the deployment configuration, but never commit registry passwords, application master keys, or private TLS keys.
A compact configuration containing the required pieces looks like this:
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteRank #2
service: myapp
image: registry.example.com/team/myapp
servers:
web:
- 203.0.113.10
proxy:
host: app.example.com
app_port: 3000
ssl: true
registry:
server: registry.example.com
username: deploy-bot
password:
- KAMAL_REGISTRY_PASSWORD
env:
clear:
RAILS_ENV: production
secret:
- SECRET_KEY_BASE
Use the exact configuration keys supported by the Kamal release you install; labels and defaults can change between releases. The important relationships are stable: service names the deployed service, image identifies the registry repository, servers lists the host, proxy.host is the public DNS name, and proxy.app_port is the container port. If app_port is omitted, the proxy default is port 80.
Registry credentials and application secrets
Put values referenced by name in .kamal/secrets. For example:
KAMAL_REGISTRY_PASSWORD=replace-with-a-registry-token
SECRET_KEY_BASE=replace-with-the-application-secret
The registry can be Docker Hub, a Git hosting registry, or a private registry. The server, username, and password reference in deploy.yml must match that registry’s authentication method. A token with only the permissions needed to push and pull the repository is safer than a personal account password.
Use env.clear for non-sensitive settings that are safe to show in the repository. Put credentials and signing keys under env.secret. Review CI logs and shell history: printing the secrets file or expanding secret values in debug output can expose them even when the file itself is ignored by Git.
Run the first bootstrap
From the repository directory, execute:
kamal setup
The first setup performs several operations in sequence:
- Connects to each configured server over SSH.
- Installs Docker on the server when it is missing and prepares the runtime.
- Logs in to the configured image registry.
- Builds the checked-out application version and pushes the image.
- Pulls the image on the VPS and starts the application container.
- Starts or configures
kamal-proxy, which listens on ports 80 and 443 and forwards requests to the application port. - Checks
GET /up. When it returns 200, proxy traffic is switched to the new container. - Stops the old container, then prunes images that are no longer needed.
Watch the output for the first failing operation. An SSH error means the key, user, address, or firewall is wrong; a registry error means the image name or credentials are wrong; a health-check error means the application is not answering the expected path and port. Fix the underlying issue and run setup again rather than manually editing containers on the server.
Rank #3
Deploy later releases without downtime
After the server has been bootstrapped, commit your application changes and run:
kamal deploy
Kamal builds the current checkout, pushes the image, starts a new container, and asks kamal-proxy to route traffic only after the new instance is healthy. Existing requests can finish on the old container while the new one starts. Once the switch succeeds, Kamal stops the old container and prunes unused images.
Useful deployment flags include:
| Flag | Use | When to choose it |
|---|---|---|
--skip-push |
Do not push an image during this invocation. | Use only when the required image is already available in the registry and you understand the tag being deployed. |
--roles |
Limit the operation to configured server roles. | Useful when the file defines separate web, worker, or accessory roles. |
--hosts |
Limit the operation to selected hosts. | Useful for a staged rollout or isolating one machine while diagnosing it. |
--destination |
Select a deployment destination. | Use when your configuration defines more than one destination. |
--config-file |
Read a different configuration file. | The default is config/deploy.yml; specify this for a separate staging or production file. |
kamal redeploy is a narrower operation for an already prepared server. It redeploys without repeating server bootstrap, proxy startup, pruning, or registry login. Use it when those setup steps are already complete and you need the deploy operation itself; use kamal setup for a new host or after a deliberately rebuilt environment.
Configure HTTPS with kamal-proxy
On a single host, setting proxy.ssl: true enables kamal-proxy to request and renew a Let’s Encrypt certificate for the configured host. The DNS name must resolve publicly to the VPS, and port 443 must be reachable so the certificate challenge can complete. Port 80 is also useful because the proxy can redirect HTTP to HTTPS.
With SSL enabled, the proxy’s default behavior includes an HTTPS redirect. Its default response timeout is 30 seconds. SSL also turns off forwarding of some headers unless forward_headers is explicitly enabled, so check your framework’s scheme and client-IP handling if the application generates absolute URLs or enforces secure cookies.
Path handling matters when the service is mounted behind a prefix. Kamal-proxy strips path prefixes by default before forwarding. Set strip_path_prefix: false when the application must receive the original prefix. Test links, redirects, and asset URLs after changing this behavior.
Rank #4
If you already own a certificate, kamal-proxy can load it from secrets using certificate_pem and private_key_pem. Keep both values in the secrets mechanism and protect their permissions. Do not paste a private key into the tracked YAML file.
Environment updates and accessories
When only environment values change, push the configured environment with:
kamal env push
Use this deliberately: changing a secret may require an application restart before the process reads the new value. Confirm how your framework loads environment variables at boot, then deploy or restart the affected service so the new configuration is active.
Free tools Windows power users keep installed
One-click scans. No signup required.
Databases, queues, and other supporting processes can be modeled as accessories. They have their own lifecycle commands. If an accessory or proxy is left in a partial state after an interrupted operation, reboot the affected component and deploy again:
kamal proxy reboot
kamal accessory reboot
kamal deploy
Run only the reboot command that matches the component you actually need to recover. A reboot can interrupt active connections, so schedule it when that interruption is acceptable.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Diagnose the failures you are most likely to see
DNS resolves to the wrong address
Symptoms include certificate challenges that never complete, browsers reaching a different server, or an HTTP request that works locally but not through the public hostname. Check the public A record and wait for caches to expire. If you use both A and AAAA records, verify that IPv6 reaches the same configured host; an incorrect AAAA record can send some clients to the wrong machine even when IPv4 is correct.
Best Value
Port 443 is blocked
Let’s Encrypt cannot validate the hostname when the challenge cannot reach the proxy. Open TCP 443 in the VPS firewall, cloud security group, and any host firewall. Confirm that another process is not already bound to the port. The proxy is designed to listen on 80 and 443, so an unrelated web server must be removed, reconfigured, or placed elsewhere.
The /up check fails
Inspect the endpoint from inside the container and from the server’s network. A 404 means the route is missing; a 301 or 302 can indicate an authentication or HTTPS redirect that the readiness check does not accept; a 500 usually indicates an application boot or dependency problem. Ensure the process listens on the declared app_port, binds to a reachable interface, and returns 200 quickly. Do not make the health endpoint depend on a full page render or an optional third-party API.
Registry authentication fails
Recheck the registry hostname, repository path, username, and token permissions. Confirm that the token can push from the deployment machine and that the server can pull the resulting image. A password reference in YAML must exactly match the key name in .kamal/secrets. Rotate the token if it has been exposed in logs or shell history.
SSH or Docker permissions fail
Test the same user and key with a direct SSH command. Verify that the user can invoke Docker without an interactive password prompt, or adjust the host’s Docker permissions according to its operating system. Check that the server has disk space and that the configured host address is the one authorized by the SSH key.
A deployment stops halfway
Read the first error rather than the final summary line. If the proxy or an accessory is partially configured, use the corresponding reboot command, correct the root cause, and run kamal deploy again. Avoid deleting containers or proxy state by hand unless you understand the consequences; manual cleanup can remove the previous healthy version that provides the safest fallback during recovery.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesOperational checklist for every release
- Confirm DNS still points to the intended VPS and that ports 22, 80, and 443 are open.
- Run the application’s tests and build the Docker image from the commit being deployed.
- Verify that
/upreturns 200 in a fresh container on the configured port. - Check that
.kamal/secretsis ignored by version control and that required keys exist on the deployment machine or CI runner. - Review the image tag and registry repository before using
--skip-push. - Run
kamal deployand watch the health-check and proxy-switch stages. - After the switch, test the public HTTPS URL, login/session behavior, background jobs, and any paths affected by proxy prefix stripping.
- Keep the previous application image until the new release has passed smoke tests; Kamal’s normal cleanup occurs after a successful switch.
What Kamal does and does not provide
Kamal gives you a repeatable SSH-driven release process, image distribution, health-gated traffic switching, and a proxy that can terminate HTTPS on a VPS. You still own the VPS operating system, firewall, backups, database durability, monitoring, alerting, and application-level rollback policy. The documented proxy defaults and command flags can change between Kamal and kamal-proxy releases, so check the release documentation when upgrading rather than assuming a newer version behaves exactly like the configuration above.
The practical decision is straightforward: choose Kamal when you want container-based, zero-planned-downtime releases while retaining control of a single VPS or a small group of hosts. If you need managed databases, autoscaling, or platform-level observability, those concerns must be added separately; they are outside Kamal’s deployment loop.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




