Service containers give you MySQL without a compose file

Integration tests need a real database, and a hosted runner is an empty machine — which is the problem service containers exist for.

jobs:
  test:
    runs-on: ubuntu-20.04
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: root
          MYSQL_DATABASE: app_test
        ports: ['3306:3306']
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-retries=5

The health options are not optional: without them the job starts before MySQL is listening, and the failure is an intermittent connection refused that only happens on a cold runner. Ports are published to the host, so the application connects to 127.0.0.1 rather than to a service name — which is the difference from a compose file and catches everyone once. A container image with an entrypoint that needs arguments cannot be a service, which is the main limitation.