← Back to posts

Part 4 - Deploy With Kamal And Add More Apps To Your Server

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:

  1. Deploy All Your Rails Apps On One Server Using Kamal
  2. Rails Setup to Deploy Your App on One Hetzner Machine
  3. Rails Docker Setup to Host your Apps on One Virtual Machine
  4. Deploy With Kamal And Add More Apps To Your Server

This is part 4.

Remaining steps

  1. Commit your changes, because that is were Kamal is going to look for changes.
  2. Run kamal server bootstrap to setup the servers
  3. Run kamal setup to setup the application dependenceis and to deploy the application

If you have changes to your app, you can redeploy with zero-downtime by just running kamal deploy

Other usefule commands

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
...

For subsequent apps

Now that we've deployed our first app, let's look at the setup for subsequenst apps, which differs slightly in that we:

  1. No longer need traefik, since that's already running from our first app
  2. need to assign different ports for the new apps' db, redis, etc.

We still need:

  1. Our domain
  2. Our cloudflare setup for the domain
  3. Docker running
  4. Everyting else from the previous articles

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

Remaining steps

  1. Commit your changes
  2. Run kamal server bootstrap
  3. Run kamal setup