docker exec attaches; docker-compose run creates

The two commands look like alternatives and do different things. exec runs a process inside a container that is already up; run starts a new container from the same image.

# inside the running app container — sees its state
docker-compose exec php php artisan tinker

# a brand new container — fresh, and gone afterwards
docker-compose run --rm php composer install

That distinction decides which one is correct. Anything inspecting the running application — a REPL against live state, reading a log, checking a mounted file — wants exec. Anything one-off wants run --rm, and the --rm is not optional or the containers accumulate silently until the disk fills. run also allocates the service’s ports by default, which conflicts with the already-running container; --no-deps and --service-ports are how that gets controlled.