In this last part of the article, we'll explore some useful commands and we'll see how we can add new, subsequent apps to our Hetzner virtual machine.
This article is structured into four seperate posts:
This is part 4.
kamal server bootstrap
to setup the serverskamal setup
to setup the application dependenceis and to deploy the applicationIf you have changes to your app, you can redeploy with zero-downtime by just running kamal deploy
In general, do make sure you familiarise yourself with Kamal commands!!
Pushing environment files to the servers
Before you can deploy, you’ll need to push env files to the servers. They will then be included in the docker run commands.
You can do this by running:
kamal env push
Interactive commands over SSH
Starts a Rails console in a new container made from the most recent app image
kamal app exec -i 'bin/rails console'
Server logs
kamal app logs -f
Commands help
Just run this to see all the available options
kamal app
Troubleshooting
Go into your VM:
ssh root@YOUR-HETZNER-IP-ADDRESS
Inside, run docker ps
to see all running docker instances, and their unique IDs
...
Now that we've deployed our first app, let's look at the setup for subsequenst apps, which differs slightly in that we:
db
, redis
, etc.We still need:
Here's what my deploy.yml
file for a subsequent app looks like:
# config/deploy.yml
---
service: my-second-app
image: fmuster/my-second-app
servers:
web:
hosts:
- MY-HETZNER-IP-ADDRESS
options:
"add-host": host.docker.internal:host-gateway
labels:
traefik.http.routers.mysecondapp.entrypoints: websecure
traefik.http.routers.mysecondapp.rule: "Host(`mysecondapp.com`) || Host(`www.mysecondapp.com`)"
traefik.http.routers.mysecondapp.tls.certresolver: letsencrypt
traefik.http.routers.mysecondapp.tls.domains[0].main: mysecondapp.com
traefik.http.routers.mysecondapp.tls.domains[0].sans: www.mysecondapp.com
worker:
hosts:
- MY-HETZNER-IP-ADDRESS
cmd: bundle exec sidekiq
registry:
username: fmuster
password:
- KAMAL_REGISTRY_PASSWORD
env:
clear:
DB_HOST: MY-HETZNER-IP-ADDRESS
POSTGRES_USER: my-second-app_user
POSTGRES_DB: my-second-app_production
redis: MY-HETZNER-IP-ADDRESS
secret:
- RAILS_MASTER_KEY
- POSTGRES_PASSWORD
- REDIS_URL
ssh:
user: root
accessories:
db:
image: postgres:16
host: MY-HETZNER-IP-ADDRESS
port: 5429:5432
env:
clear:
POSTGRES_USER: my-second-app_user
POSTGRES_DB: my-second-app_production
secret:
- POSTGRES_PASSWORD
directories:
- data:/var/lib/postgresql/data
redis:
image: redis:7.0
roles:
- web
- worker
port: 6382:6379
directories:
- data:/data
Notice that I no longer include traefik
but just the relevant information for my app's domain. I'm also reassigning the ports of the db
and of redis
to different ones from the first app. You'll need to keep track of your ports, since every new app you're adding to the VM will need it's own ports.
For my database.yml
setup, I need to add a new line, corresponding to the new db
port I have assigned above:
production:
<<: *default
host: '<%= ENV["DB_HOST"] %>'
database: '<%= ENV["POSTGRES_DB"] %>'
username: '<%= ENV["POSTGRES_USER"] %>'
password: '<%= ENV["POSTGRES_PASSWORD"] %>'
port: 5429 # < -- This is important
Similarly, in my .env
file I'll change the REDIS_URL
to the new port:
REDIS_URL=redis://MY-HETZNER-IP-ADDRESS:6382/1
kamal server bootstrap
kamal setup