Introduction
Docker ensures consistent environments across development and production. Rails 8 generates a production-ready Dockerfile automatically, using multi-stage builds and Thruster for HTTP compression and asset serving.
Key Concepts
- Multi-Stage Build: A Dockerfile pattern that uses a build stage for compilation and a final stage with only runtime dependencies, producing smaller images.
- Thruster: A lightweight HTTP proxy included in Rails 8 that handles gzip compression, X-Sendfile, and cache headers — replacing the need for nginx in simple deployments.
- Docker Entrypoint: A script that runs before the main process, typically used to run database migrations on deploy.
Real World Context
Rails 8 generates a production Dockerfile when you create a new app. This Dockerfile uses multi-stage builds to keep the final image small and includes Thruster for serving assets efficiently — no separate nginx or CDN needed for most apps.
Deep Dive
Rails 8 Dockerfile
dockerfileFROM ruby:3.4-slim AS base WORKDIR /rails RUN apt-get update -qq && \ apt-get install --no-install-recommends -y \ curl libpq-dev libvips \ && rm -rf /var/lib/apt/lists/* FROM base AS build RUN apt-get update -qq && \ apt-get install --no-install-recommends -y \ build-essential git pkg-config \ && rm -rf /var/lib/apt/lists/* COPY Gemfile Gemfile.lock ./ RUN bundle config set --local deployment true && \ bundle config set --local without 'development test' && \ bundle install && \ rm -rf ~/.bundle/ vendor/bundle/ruby/*/cache COPY . . RUN SECRET_KEY_BASE=dummy ./bin/rails assets:precompile FROM base COPY --from=build /rails /rails COPY --from=build /usr/local/bundle /usr/local/bundle RUN useradd rails --create-home --shell /bin/bash && \ chown -R rails:rails /rails USER rails:rails ENTRYPOINT ["/rails/bin/docker-entrypoint"] EXPOSE 3000 # Thruster wraps the Rails server for compression and asset serving CMD ["./bin/thrust", "./bin/rails", "server"]
Docker Compose (Rails 8 — No Redis Needed)
yamlservices: web: build: . ports: - '3000:3000' environment: - DATABASE_URL=postgres://postgres:password@db:5432/app_production - RAILS_MASTER_KEY - SOLID_QUEUE_IN_PUMA=true depends_on: - db db: image: postgres:17 volumes: - postgres_data:/var/lib/postgresql/data environment: POSTGRES_PASSWORD: password volumes: postgres_data:
Notice: no Redis service, no separate Sidekiq worker. Solid Queue runs embedded in Puma.
Docker Entrypoint
bash#!/bin/bash set -e if [ "$RUN_MIGRATIONS" = "true" ]; then ./bin/rails db:migrate fi exec "$@"
Common Pitfalls
- Adding a separate Sidekiq container when using Solid Queue — With
SOLID_QUEUE_IN_PUMA=true, jobs run inside the web process. No separate worker container needed for most apps. - Using an old Ruby base image — Rails 8.1 requires Ruby 3.2+. Use
ruby:3.4-slimfor the latest stable release.
Best Practices
- Use Thruster in the CMD —
CMD ["./bin/thrust", "./bin/rails", "server"]wraps your server with compression and efficient asset serving. - Keep Docker Compose simple — Rails 8's Solid Trifecta means you only need a database container for most apps.
Summary
- Rails 8 generates a production-ready Dockerfile with multi-stage builds.
- Thruster handles HTTP compression and asset serving — no nginx needed.
- Docker Compose only needs a database — no Redis or separate worker containers.
- Use
SOLID_QUEUE_IN_PUMA=trueto run background jobs inside the web process.
Code Examples
bash
# Rails 8 production Docker CMD uses Thruster
CMD ["./bin/thrust", "./bin/rails", "server"]
# Thruster provides:
# - Gzip/Brotli compression for all responses
# - X-Sendfile for efficient file serving
# - Cache headers for fingerprinted assets
# - No nginx or separate proxy needed