Skip to content

Docker Dev/Kamal Deployment - #6

Merged
wilbert merged 7 commits into
wilbert-ribeiro-devfrom
feat/deployment
Sep 10, 2026
Merged

wilbert merged 7 commits into
wilbert-ribeiro-devfrom
feat/deployment

Conversation

@wilbert

@wilbert wilbert commented Sep 10, 2026

Copy link
Copy Markdown
Owner

PR 7: Deployment

Multi-stage Dockerfile, Kamal 2 config, linters, and CI.

Topology: one accessory, no Redis

Solid Queue, Solid Cache and Solid Cable all run on the single Postgres
accessory, as four logical databases. Production is one database container
plus two app roles (web, job). No Redis anywhere in the stack.

config/postgres/init.sql bootstraps the queue/cache/cable databases; the
accessory only creates the primary.

Job process

Solid Queue runs as a separate Kamal role, not SOLID_QUEUE_IN_PUMA. In-Puma
is one less container, but it puts a 50k-row spreadsheet import in contention
with request threads, which is what the dedicated imports queue in PR 5
exists to avoid.

Docker

  • Multi-stage: base -> build (gems, npm, Vite, bootsnap) -> slim final.
  • npm ci copies only package*.json first so application changes do not
    invalidate the npm layer. Same for the Gemfile.
  • libvips over ImageMagick for avatar variant processing.
  • Non-root rails user.
  • SECRET_KEY_BASE=DUMMY during precompile only. The real
    RAILS_MASTER_KEY arrives at runtime via Kamal secrets and is never baked
    into a layer; config/master.key is in .dockerignore.
  • Thruster fronts Puma for asset caching, compression and X-Sendfile.

Active Storage

S3, not a container volume. Local disk loses every uploaded avatar on deploy,
and a volume mount pins the app to one host. Bucket is private; variants are
served through Rails.

Inertia SSR

The SSR bundle is built and shipped but gated behind INERTIA_SSR_ENABLED,
default off. SSR adds a third process and a failure mode where a dead Node
server 500s every page. Documented as opt-in rather than presented as
production-ready.

Linting

rubocop-rails-omakase as the baseline plus rubocop-rspec and targeted
overrides, rather than a bespoke config. Omakase is the Rails-official
baseline; a hand-rolled 400-line config is a maintenance liability I would not
defend in review. Brakeman and bundler-audit run in CI with non-zero exit on
findings. tsc --noEmit is included because Vite transpiles TypeScript
without type-checking it.

ZJIT

Benchmarked, not assumed. See README for measured rows/sec on a 10k-row import
with ZJIT on, YJIT on, and both off. [Fill in the actual numbers and the
resulting default.]

Test plan

  • bin/ci locally
  • docker build . and docker run against a local Postgres
  • kamal setup against a staging host, then kamal deploy

@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 1abf23a6-018a-4607-9801-999bb0df013f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@wilbert
wilbert merged commit 79aa767 into wilbert-ribeiro-dev Sep 10, 2026
0 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant