Kamal Config Generator

config/deploy.yml
# Name of your application. Used to uniquely configure containers.
service: <SERVICE_NAME>

# Name of the container image.
image: <DOCKER_USERNAME>/<IMAGE_NAME>

# Deploy to these servers.
servers:
  web:
    hosts:
      - <SERVER_IP_ADDRESS>
    labels:
      traefik.http.routers.<DOMAIN>.entrypoints: websecure
      traefik.http.routers.<DOMAIN>.rule: 'Host(`<DOMAIN_NAME>`) || Host(`<DOMAIN_NAME_WITH_WWW>`)'
      traefik.http.routers.<DOMAIN>.tls.certresolver: letsencrypt
    options:
      network: 'private'
# Credentials for your image host.
registry:
  username: <DOCKER_USERNAME>

  # Always use an access token rather than real password when possible.
  password:
    - KAMAL_REGISTRY_PASSWORD
# Inject ENV variables into containers (secrets come from .env).
# Remember to run `kamal env push` after making changes!
env:
  clear:
    HOSTNAME: <DOMAIN_NAME>
DB_HOST: <SERVER_IP_ADDRESS> POSTGRES_USER: <APP_NAME>_user POSTGRES_DB: <APP_NAME>_production RAILS_SERVE_STATIC_FILES: true RAILS_LOG_TO_STDOUT: true
secret: - RAILS_MASTER_KEY
- POSTGRES_PASSWORD
# Use a different ssh user than root # ssh: # user: app volumes: - '/storage:/rails/storage'
accessories: db: image: postgres:16 host: <SERVER_IP_ADDRESS> port: 5432 env: clear: DB_HOST: <SERVER_IP_ADDRESS> POSTGRES_USER: <APP_NAME>_user POSTGRES_DB: <APP_NAME>_production secret: - POSTGRES_PASSWORD directories: - data:/var/lib/postgresql/data
# Configure custom arguments for Traefik traefik: options: publish: - '443:443' volume: - '/letsencrypt/acme.json:/letsencrypt/acme.json' network: 'private' args: entryPoints.web.address: ':80' entryPoints.websecure.address: ':443' entryPoints.web.http.redirections.entryPoint.to: websecure entryPoints.web.http.redirections.entryPoint.scheme: https entryPoints.web.http.redirections.entrypoint.permanent: true certificatesResolvers.letsencrypt.acme.email: '<YOUR_EMAIL_ADDRESS>' certificatesResolvers.letsencrypt.acme.storage: '/letsencrypt/acme.json' certificatesResolvers.letsencrypt.acme.httpchallenge: true certificatesResolvers.letsencrypt.acme.httpchallenge.entrypoint: web
config/database.yml
# SQLite. Versions 3.8.0 and up are supported.
#   gem install sqlite3
#
#   Ensure the SQLite 3 gem is defined in your Gemfile
#   gem "sqlite3"
#
default: &default
  adapter: sqlite3
  pool: 5
  timeout: 5000

development:
  <<: *default
  database: storage/<APP_NAME>_development.sqlite3

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: storage/<APP_NAME>_test.sqlite3

production:
  <<: *default
  database: storage/<APP_NAME>_production.sqlite3
# PostgreSQL. Versions 9.3 and up are supported.
#
# Install the pg driver:
#   gem install pg
# On macOS with Homebrew:
#   gem install pg -- --with-pg-config=/usr/local/bin/pg_config
# On Windows:
#   gem install pg
#       Choose the win32 build.
#       Install PostgreSQL and put its /bin directory on your path.
#
# Configure Using Gemfile
# gem "pg"
#
default: &default
  adapter: postgresql
  encoding: unicode
  # For details on connection pooling, see Rails configuration guide
  # https://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: <APP_NAME>_development

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: <APP_NAME>_test

production:
  <<: *default
  host: '<%= ENV["DB_HOST"] %>'
  database: '<%= ENV["POSTGRES_DB"] %>'
  username: '<%= ENV["POSTGRES_USER"] %>'
  password: '<%= ENV["POSTGRES_PASSWORD"] %>'
Dockerfile
# syntax = docker/dockerfile:1

# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.2.2
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base

# Rails app lives here
WORKDIR /rails

# Set production environment
ENV RAILS_ENV="production" \
    BUNDLE_DEPLOYMENT="1" \
    BUNDLE_PATH="/usr/local/bundle" \
    BUNDLE_WITHOUT="development"

# Throw-away build stage to reduce size of final image
FROM base as build

# Install packages needed to build gems
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y build-essential git libvips pkg-config 

# Install application gems
COPY Gemfile Gemfile.lock ./
RUN bundle install && \
    rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
    bundle exec bootsnap precompile --gemfile

# Copy application code
COPY . .

# Precompile bootsnap code for faster boot times
RUN bundle exec bootsnap precompile app/ lib/

# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile

# Final stage for app image
FROM base

# Install packages needed for deployment
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y curl libsqlite3-0 libvips && \
    rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Copy built artifacts: gems, application
COPY --from=build /usr/local/bundle /usr/local/bundle
COPY --from=build /rails /rails

# Run and own only the runtime files as a non-root user for security
RUN useradd rails --create-home --shell /bin/bash && \
    chown -R rails:rails db log storage tmp
USER rails:rails

# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]

# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD ["./bin/rails", "server"]
.env
KAMAL_REGISTRY_PASSWORD=dckr_pat_xxxxxxxxxxxxxxxxxxxxxxxxxxx
RAILS_MASTER_KEY=0728xxxxxxxxxxxxxxxxxxxxxxxxc4efPOSTGRES_PASSWORD=passxxxxxxxxxxxxxxxxxxxxxxxxword