Dependency injection helps making your code maintainable, by moving the wiring of the way dependencies are wired to a single place in the application (the start-up path a.k.a. Composition Root). This is great, since that Composition Root has (just as everything else in your application) a single responsibility.
When your application will evolve and grow, you will see that classes themselves don't get much more complex by themselves (when adhering to the SOLID principles). The Composition Root however, will get big and complex very quickly, especially when using decorators to add behavior and doing things like registration by convention.
This is where a DI container comes in play. While the SOLID principles help you in designing an application that is flexible and maintainable, a DI container will help you keep the Composition Root maintainable. That's its sole purpose. Everything you do with a container can be done without it, but this will often result in a lot of repetitive and boilerplate code that is hard to read and hard to maintain.
So again, the sole purpose of a DI container is to make the composition root maintainable.