partydrone PRO said over 2 years ago on Docker on Rails 7 :
If the only reason for having the `base` service is to share configuration, I believe a best practice is to create a custom root-level definition beginning with `x-` and using that.

Also worth noting is the difference between "mapping" ports vs "exposing" ports in a Compose file. When mapping a port (e.g., `- 3000:3000` for the Rails app), you are mapping the port on your local machine to a port on the container. If you only expose a port (e.g., `- 5432` for the database service), Compose maps a random port on your machine to the port on the container. To determine what that random port is, you can run `docker compose ps`.

In the end, your docker-compose.yml file could look like this:

version: "3.8"

x-app: &app
  build:
    args:
      RUBY_VERSION: "3.0.2-apline"
    context: .
    dockerfile: Dockerfile.dev
  depends_on:
    - db
    - redis
  environment:
    - DATABASE_URL=postgres://postgres:postgres@db:5432
  stdin_open: true
  tty: true
  volumes:
    - .:/app:cached
    - bundle:/usr/local/bundle
    - node_modules:/app/node_modules

services:
  db:
    environment:
      - POSTGRES_PASSWORD=postgres
    image: postgresql:14-alpine
    ports:
      - 5432
    restart: always
    volumes:
      - postgres:/var/lib/postgresql/data

  redis:
    image: redis:alpine
    restart: always
  
  web:
    <<: *app
    command: rails s -b 0.0.0.0
    ports:
      - 3000:3000

  css:
    <<: *app
    command: yarn build:css --watch

  js:
    <<: *app
    command: yarn build --watch

volumes:
  bundle:
  node_modules:
  postgres: