Skip to content

feat: run the app in Docker Compose with a persistent database - #88

Merged
leouofa merged 3 commits into
mainfrom
dockerize
Sep 26, 2026
Merged

leouofa merged 3 commits into
mainfrom
dockerize

Conversation

@leouofa

@leouofa leouofa commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

Runs the web app, background jobs, the Tailwind watcher, and PostgreSQL as a Docker Compose stack, with the database surviving restarts and container recreates.

docker compose up -d --build
docker compose logs -f web

The app comes up on http://localhost:3000, and the first boot prints the seeded admin login. See docs/DOCKER.md.

Persistence

The database lives in the named volume macronx_pg_data, mounted at /var/lib/postgresql and not the older /var/lib/postgresql/data: Postgres 18 sets PGDATA to /var/lib/postgresql/18/docker, so mounting the old path leaves the data in the container layer where it is silently lost on every recreate. Only docker compose down -v destroys it. Uploaded files persist through the working tree bind mount.

Changes

  • Dockerfile.dev (new) — development image with the full bundle, plus ffmpeg for the audio pipeline and ImageMagick with libheif for Image::ConvertHeicToJpegJob, which mini_magick needs for its convert binary. Leaves RAILS_ENV unset on purpose: baking in development would defeat ENV['RAILS_ENV'] ||= 'test' in spec/rails_helper.rb and run the suite against macron_x_development.
  • compose.yaml (new) — db, web, jobs, and css services. web sets BINDING=0.0.0.0 because Rails binds to localhost in development, which is unreachable from outside a container. jobs waits on the web health check so the two never race to create the same database.
  • config/database.yml — reads PGHOST/PGPORT/PGUSER/PGPASSWORD, all unset by default, so local development keeps using the unix socket. Plain PG* variables are not sufficient on their own: Rails always passes a nil host, and libpq then ignores PGHOST. *_DATABASE_URL was avoided deliberately, since it also overrides the test database.
  • db/seeds.rb — SEED_ADMIN_EMAIL and SEED_ADMIN_PASSWORD now work in every environment, so a container can be given a known login. Still idempotent.
  • Docs — docs/DOCKER.md, linked from the README and SETUP.md, which now reflects the seed variables.

Also in this branch: tags:renumber_id

Unrelated to Docker, but pushed to this branch rather than a PR of its own: a rake task for changing a tag's id.

bin/rails tags:renumber_id OLD_ID=9 NEW_ID=14
# Tag 'reddit' is now id 14.
#   inboxes.tag_id: 1 row(s) remapped.
#   workflows.tag_id: 1 row(s) remapped.

update_column(:id, ...) on its own cannot work: inboxes and workflows both hold foreign keys to tags.id, so the children cannot point at the new id before it exists, and the parent cannot move while children still point at the old one — Postgres raises ForeignKeyViolation either way. Tags::RenumberId reads the referencing constraints out of pg_constraint rather than hardcoding a table list, then drops them, remaps the rows, moves the primary key, and restores them inside one transaction. It runs with requires_new: true so a mid-flight failure rolls back only the remap. Invalid input aborts before touching anything.

docs/MAINTENANCE.md gains a "Changing a tag's id" section covering the arguments, the cases that write nothing, and the foreign key ordering problem. The motivating case is the inbox filter, which carries a tag id in the URL (/inboxes?tag=9), so a bookmarked filter link breaks once the id means a different tag in a restored or migrated database.

Verified

  • 270 examples, 0 failures (bundle exec rspec in the container)
  • bin/rubocop clean across 129 files
  • Data survives docker compose down followed by up; the second boot's db:prepare is a no-op
  • Devise sign-in and the dashboard render end to end
  • The host's local PostgreSQL is untouched by the containerized suite
  • tags:renumber_id run end to end against the test database, plus all five rejected-input paths against development, which write nothing

Note

Unrelated to this change but found while testing: the production Dockerfile installs libvips but not ImageMagick, so Image::ConvertHeicToJpegJob cannot shell out to convert there. Left alone here since the production image is out of scope, but worth a follow-up if IMINT runs in production.

Adds a development container stack so the web app, background jobs, the
Tailwind watcher, and PostgreSQL run together with the database surviving
restarts and recreates.

The data volume is mounted at /var/lib/postgresql rather than the older
/var/lib/postgresql/data, because Postgres 18 sets PGDATA to
/var/lib/postgresql/18/docker: mounting the old path leaves the data in the
container layer, where it is silently lost on every recreate.

config/database.yml now reads PGHOST/PGPORT/PGUSER/PGPASSWORD. They are
unset by default, so local development keeps using the unix socket. Plain
PG* variables are not enough on their own, because Rails always passes a
host of nil and libpq then ignores PGHOST.

Seeds accept SEED_ADMIN_EMAIL and SEED_ADMIN_PASSWORD in every environment
so a container can be given a known login.

Docs in docs/DOCKER.md, linked from the README and SETUP.md.
Moving a tag to a different id needs the referencing rows updated too, and
plain update_column fails: the child rows cannot point at the new id before
it exists, and the parent cannot move while children still point at the old
one. Tags::RenumberId discovers every foreign key that references tags.id,
then drops those constraints, remaps the rows, moves the primary key, and
restores the constraints inside a single transaction.

Usage: bin/rails tags:renumber_id OLD_ID=9 NEW_ID=14
The task has no coverage outside its own spec, and it is the kind of
operation someone only reaches for when ids have already drifted, so
documenting the accepted arguments, the cases that write nothing, and the
foreign key ordering problem it works around matters more here than usual.
@leouofa
leouofa merged commit 8b547b4 into main Sep 26, 2026
3 checks passed
@leouofa
leouofa deleted the dockerize branch September 26, 2026 22:02
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