Traits.xyz – Beautiful way to browse NFT collections

Submitted by @marckohlbrugge

Kamal deploy.yml

# Name of your application. Used to uniquely configure containers.
service: traits

# Name of the container image.
image: secret123/traits

# Deploy to these servers.
servers:
  web:
    hosts:
      - 123.123.123.123
    labels:
      traefik.http.routers.traits-web.rule: Host(`traits.xyz`)
      traefik.http.routers.traits-web.entrypoints: http
  sidekiq:
    hosts:
      - 123.123.123.123
    cmd: bundle exec sidekiq -C config/sidekiq.yml

# Credentials for your image host.
registry:
  # Specify the registry server, if you're not using Docker Hub
  server: registry.digitalocean.com
  username: [email protected]

  # 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:
    WEB_CONCURRENCY: 2
    DB_POOL: 10
    RAILS_MAX_THREADS: 5
    ADMIN_USERNAME: secret123
    ADMIN_PASSWORD: secret123
    PGHERO_USERNAME: secret123
    PGHERO_PASSWORD: secret123
  secret:
    - RAILS_MASTER_KEY
    - DATABASE_URL
    - REDIS_URL
    - REDIS_CACHE_URL
    - BUNDLE_ENTERPRISE__CONTRIBSYS__COM
    - PGHERO_DATABASE_URL

# Configure builder setup.
builder:
  remote:
    arch: amd64
    host: ssh://[email protected]

  secrets:
    - BUNDLE_ENTERPRISE__CONTRIBSYS__COM

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 libpq-dev libvips pkg-config

# Packages for rbsecp256k1 gem
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y automake pkg-config libtool libffi-dev libssl-dev libgmp-dev python3-dev

# Install application gems
COPY Gemfile Gemfile.lock ./
RUN --mount=type=secret,id=BUNDLE_ENTERPRISE__CONTRIBSYS__COM \
    BUNDLE_ENTERPRISE__CONTRIBSYS__COM=$(cat /run/secrets/BUNDLE_ENTERPRISE__CONTRIBSYS__COM) \
    bundle install && \
    rm -rf /usr/local/bundle/cache

# 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 libvips postgresql-client && \
    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.example file

<% if (session_token = `op signin --account secret123 --raw`.strip) %># Generated by kamal envify
KAMAL_REGISTRY_PASSWORD=<%= `op read "op://Code/traits/KAMAL_REGISTRY_PASSWORD" -n` %>
RAILS_MASTER_KEY=<%= `op read "op://Code/traits/RAILS_MASTER_KEY" -n` %>
DATABASE_URL=<%= `op read "op://Code/traits/DATABASE_URL" -n` %>
REDIS_URL=<%= `op read "op://Code/traits/REDIS_URL" -n` %>
REDIS_CACHE_URL=<%= `op read "op://Code/traits/REDIS_CACHE_URL" -n` %>
PGHERO_DATABASE_URL=<%= `op read "op://Code/traits/PGHERO_DATABASE_URL" -n` %>
BUNDLE_ENTERPRISE__CONTRIBSYS__COM=<%= `op read "op://Code/traits/BUNDLE_ENTERPRISE__CONTRIBSYS__COM" -n` %>
<% else raise ArgumentError, "Session token missing" end %>

Explanation

I manage the web server and Sidekiq with Kamal. I use managed databases, so that's why I'm not using any accessories.

`BUNDLE_ENTERPRISE__CONTRIBSYS__COM` is my license key for Sidekiq Enterprise. It's required during build to install the gems. (Note that you also need to change your Dockerfile to pass this secret).

I use .env.erb with 1Password (with `kamal envify`) to generate my `.env` file.