Skip to content

Instantly share code, notes, and snippets.

@lcorneliussen
Last active March 4, 2025 08:23
Show Gist options
  • Select an option

  • Save lcorneliussen/a6384d8b9ea84d06b8a367f579f81ed0 to your computer and use it in GitHub Desktop.

Select an option

Save lcorneliussen/a6384d8b9ea84d06b8a367f579f81ed0 to your computer and use it in GitHub Desktop.
BT docker setup
....
deploy:
name: 🚢 Deploy to Production
needs: [minitest, standardrb, db_schema]
runs-on: ubuntu-latest
env:
DOCKER_BUILDKIT: 1
RAILS_ENV: production
RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
KAMAL_REGISTRY_PASSWORD: ${{ secrets.KAMAL_REGISTRY_PASSWORD }}
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
SECRET_KEY_BASE: ${{ secrets.SECRET_KEY_BASE }}
REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }}
HONEYBADGER_API_KEY: ${{ secrets.HONEYBADGER_API_KEY }}
POSTMARK_API_TOKEN: ${{ secrets.POSTMARK_API_TOKEN }}
GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }}
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Install Kamal
run: gem install kamal
- uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Set up Docker Buildx for cache
uses: docker/setup-buildx-action@v3
- name: Remove existing SECRETS line from .kamal/secrets
run: sed -i '/^SECRETS=/d' .kamal/secrets
- name: Update environment variables in .kamal/secrets
run: sed -i 's/^\([A-Z_]*\)=.*/\1=$\1/' .kamal/secrets
- name: Check Kamal version
run: kamal version
- name: Free up disk space
run: kamal prune all
- name: Login to Docker registry
run: kamal registry login --verbose
- name: Release deployment lock
run: kamal lock release --verbose
- name: Deploy application
run: kamal redeploy --verbose
SECRETS=$(kamal secrets fetch --adapter 1password --account ,,,, --from MyApp/KamalSecrets KAMAL_REGISTRY_PASSWORD RAILS_MASTER_KEY POSTGRES_PASSWORD SECRET_KEY_BASE REDIS_PASSWORD HONEYBADGER_API_KEY POSTMARK_API_TOKEN GOOGLE_MAPS_API_KEY)
KAMAL_REGISTRY_PASSWORD=$(kamal secrets extract KAMAL_REGISTRY_PASSWORD $SECRETS)
RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY $SECRETS)
POSTGRES_PASSWORD=$(kamal secrets extract POSTGRES_PASSWORD $SECRETS)
SECRET_KEY_BASE=$(kamal secrets extract SECRET_KEY_BASE $SECRETS)
REDIS_PASSWORD=$(kamal secrets extract REDIS_PASSWORD $SECRETS)
HONEYBADGER_API_KEY=$(kamal secrets extract HONEYBADGER_API_KEY $SECRETS)
POSTMARK_API_TOKEN=$(kamal secrets extract POSTMARK_API_TOKEN $SECRETS)
GOOGLE_MAPS_API_KEY=$(kamal secrets extract GOOGLE_MAPS_API_KEY $SECRETS)
service: bt-app
# Name of the container image.
image: bt-app/bt-app
# https://guillaumebriday.fr/how-to-deploy-rails-with-kamal-postgresql-sidekiq-and-backups-on-a-single-host
# Deploy to these servers.
servers:
web:
hosts:
- <serverip>
job:
hosts:
- <serverip>
cmd: bundle exec sidekiq
# Enable SSL auto certification via Let's Encrypt (and allow for multiple apps on one server).
# Set ssl: false if using something like Cloudflare to terminate SSL (but keep host!).
proxy:
ssl: true
host: <host>
healthcheck:
interval: 12
timeout: 5
# Credentials for your image host.
registry:
# Specify the registry server, if you're not using Docker Hub
server: ghcr.io
username: my-user
# Always use an access token rather than real password (pulled from .kamal/secrets).
password:
- KAMAL_REGISTRY_PASSWORD
# Configure builder setup.
builder:
arch: amd64
args:
NODE_OPTIONS: "--max-old-space-size=4096"
BUNDLE_BUILD__WDM: "--with-cflags=-Wno-implicit-function-declaration"
cache:
type: registry
# Inject ENV variables into containers (secrets come from .kamal/secrets).
env:
clear:
POSTGRES_HOST: 'bt-app-postgres17'
POSTGRES_PORT: '5432'
REDIS_HOST: 'bt-app-redis'
RAILS_ENV: 'production'
RAILS_SERVE_STATIC_FILES: 'true'
RAILS_LOG_TO_STDOUT: 'true'
APP_HOST: '<host>'
DEVELOPER_EMAILS: '...'
HIDE_THINGS: 'true'
secret:
- SECRET_KEY_BASE
- REDIS_PASSWORD
- POSTGRES_PASSWORD
- HONEYBADGER_API_KEY
- POSTMARK_API_TOKEN
- GOOGLE_MAPS_API_KEY
volumes:
- "app_storage:/app/storage"
# Bridge fingerprinted assets, like JS and CSS, between versions to avoid
# hitting 404 on in-flight requests. Combines all files from new and old
# version inside the asset_path.
#
# asset_path: /app/public/assets
# Configure rolling deploys by setting a wait time between batches of restarts.
#
# boot:
# limit: 10 # Can also specify as a percentage of total hosts, such as "25%"
# wait: 2
# Use accessory services (secrets come from .kamal/secrets).
#
accessories:
postgres17:
image: postgis/postgis:17-3.4
roles:
- web
- job
env:
clear:
POSTGRES_USER: "..."
POSTGRES_DB: "..."
secret:
- POSTGRES_PASSWORD
port: 5433
files:
- config/init.sql:/docker-entrypoint-initdb.d/setup.sql
directories:
- data:/var/lib/postgresql/data
redis:
image: redis:7.0
port: 6379
roles:
- web
- job
cmd: "/bin/sh -c 'redis-server --requirepass $REDIS_PASSWORD'"
volumes:
- /var/lib/redis:/data
env:
secret:
- REDIS_PASSWORD
name: bt-app
services:
db:
image: postgis/postgis:17-3.4
restart: always
volumes:
- ./storage/db:/var/lib/postgresql/data
environment:
- POSTGRES_USER=localdev
- POSTGRES_PASSWORD=localdev
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U localdev"]
interval: 1s
timeout: 5s
retries: 10
redis:
image: redis:alpine
restart: always
ports:
- "6379:6379"
volumes:
- ./storage/redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 1s
timeout: 3s
retries: 30
# syntax = docker/dockerfile:1
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.4.1
FROM ruby:$RUBY_VERSION-slim AS base
# Rails app lives here
WORKDIR /rails
# Set production environment
ENV BUNDLE_DEPLOYMENT="1" \
BUNDLE_PATH="/usr/local/bundle" \
BUNDLE_WITHOUT="development:test" \
RAILS_ENV="production"
# Update gems and bundler
RUN gem update --system --no-document && \
gem install -N bundler
# Install packages needed to install nodejs and charlock_holmes
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y curl libicu-dev && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Install Node.js
ARG NODE_VERSION=22.12.0
ENV PATH=/usr/local/node/bin:$PATH
RUN curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ && \
/tmp/node-build-master/bin/node-build "${NODE_VERSION}" /usr/local/node && \
rm -rf /tmp/node-build-master
# Throw-away build stage to reduce size of final image
FROM base AS build
# Install packages needed to build gems and node modules
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential libpq-dev libvips node-gyp pkg-config python-is-python3 git
# Install yarn
ARG YARN_VERSION=4.2.2
RUN corepack enable && \
corepack prepare yarn@$YARN_VERSION --activate
# Build options
ENV PATH="/usr/local/node/bin:$PATH"
# Install application gems
COPY Gemfile Gemfile.lock .ruby-version ./
RUN bundle install --jobs=1
RUN bundle exec bootsnap precompile --Gemfile
RUN rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git
# Install node modules
COPY package.json yarn.lock ./
RUN yarn install --immutable
# 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 imagemagick libvips postgresql-client && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Copy built artifacts: gems, application
COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
COPY --from=build /rails /rails
# Run and own only the runtime files as a non-root user for security
RUN groupadd --system --gid 1000 rails && \
useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
mkdir -p /rails/storage && \
mkdir -p /rails/tmp/pids && \
chown -R 1000:1000 db log storage tmp
USER 1000:1000
# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
# Start the server by default, this can be overwritten at runtime
#ENV PORT=80
EXPOSE 80
CMD ["bundle", "exec", "thrust", "./bin/rails", "server"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment