I'm learning CI/CD with GitHub Actions after containerizing a Django application. This workflow runs Django tests against a PostgreSQL service, then builds and pushes the application image to GitHub Container Registry when the tests pass. I'd appreciate feedback on the workflow structure, security, performance, image tagging, and anything else I should improve.
The workflow currently runs on every push, uses Python 3.13.5, installs dependencies from Backend/requirement.txt, starts PostgreSQL 14 as a service, runs python manage.py test, and builds an image tagged with the commit SHA. The test environment currently includes DEBUG=True, ALLOWED_HOST=*, and a hardcoded database password.
4 Answers
You are testing the Django source code, but not the Docker image you actually plan to run. Consider adding a separate image validation step after building: start the image with the required environment variables and database connection, run a health check or smoke test, and then push it only if that succeeds.
Running PostgreSQL as a service in CI is not automatically wrong. It is often a good way to run repeatable integration tests, especially for a smaller application. In a larger organization, you may need to weigh startup time, database isolation, network controls, and security policies against using a dedicated test database. The important distinction is that this is integration testing, not a replacement for fast unit tests.
A few solid improvements stand out. Add pip caching to setup-python with cache: 'pip' so later runs do not reinstall every dependency from scratch. Keeping the image tagged with the commit SHA is good because it gives you immutable, easy-to-rollback images; just make sure your deployment process knows which SHA to deploy. You might also add a human-friendly tag such as a release tag if that fits your deployment process.
The package write permission is correctly limited to the image job rather than the entire workflow. Also, DEBUG=True and ALLOWED_HOST=* are acceptable for an isolated test environment, but document that they must never be copied into production settings or baked into the image.
The workflow appears to run only on push because of the current trigger. If you also run it for pull requests, consider adding concurrency so obsolete runs are cancelled when newer commits arrive. Otherwise, several builds can consume runners at the same time.
You may also want to restrict image publishing to trusted branches or release events. Running tests for every pull request is useful, but publishing an image from every branch can create unnecessary registry artifacts.
The database password is written directly in the workflow. Even though this is a test database, avoid normalizing that pattern. Use a secret or a generated disposable password, and make sure credentials never appear in logs. The same general advice applies to application settings that might later be reused outside CI.
Also review your dependency installation. Installing into the system environment works on a hosted runner, but using a virtual environment or installing from a locked dependency file can make builds more reproducible.

That makes sense. I was treating the PostgreSQL service as part of the test environment, but I’ll separate the fast unit tests from the database-backed integration tests and add a smoke test for the built image.