Development Workflow with Docker
Learn how to use Docker effectively during application development, testing, and deployment.
Development Workflow with Docker
Docker is not just a deployment tool.
Many developers use Docker throughout the entire software development lifecycle, from writing code locally to deploying applications in production.
A well-structured Docker workflow provides:
- Consistent development environments
- Easier onboarding
- Simplified testing
- Reduced deployment issues
- Better team collaboration
A Typical Docker Workflow
A common development workflow looks like this:
Write Code
│
▼
Build Image
│
▼
Run Container
│
▼
Test Changes
│
▼
Commit Code
│
▼
Deploy
Docker helps ensure that each stage behaves consistently across environments.
Development vs Production Containers
Development and production containers often have different requirements.
Development
Focus on:
- Fast iteration
- Live reloading
- Debugging
- Source code access
Example:
services:
app:
build: .
volumes:
- .:/app
Changes made on the host machine appear immediately inside the container.
Production
Focus on:
- Stability
- Security
- Performance
- Immutable deployments
Example:
services:
app:
image: my-app:latest
Production containers should not rely on local source code mounts.
Using Bind Mounts
Bind mounts are one of the most useful development features.
Example:
services:
app:
build: .
volumes:
- .:/app
This maps:
Host Project Directory
│
▼
Container /app Directory
When files change locally, the container immediately sees those changes.
This is ideal for:
- Node.js applications
- Python applications
- PHP applications
- Frontend frameworks
Live Reloading
Most modern frameworks support automatic reloads.
Examples include:
- Next.js
- React
- Vue
- Express
- Flask
- FastAPI
With source code mounted into the container:
volumes:
- .:/app
the application can reload automatically whenever files change.
This eliminates the need to rebuild the image after every modification.
Using .dockerignore
When Docker builds an image, it sends files from the current directory to the Docker daemon.
Some files should never be included.
Create a .dockerignore file:
node_modules
.git
.env
dist
coverage
Benefits:
- Faster builds
- Smaller build contexts
- Improved security
A well-maintained .dockerignore is just as important as a .gitignore.
Rebuilding Images
Sometimes changes require rebuilding the image.
Examples:
- Updating dependencies
- Modifying the Dockerfile
- Installing new packages
Rebuild the image:
docker compose build
Or rebuild and restart:
docker compose up --build
Accessing Running Containers
Sometimes debugging requires direct access.
Open a shell:
docker compose exec app sh
Or:
docker compose exec app bash
depending on the image.
This allows you to:
- Inspect files
- Check environment variables
- Run commands manually
- Debug issues
Viewing Logs
Application logs are essential for troubleshooting.
View logs:
docker compose logs
Follow logs:
docker compose logs -f
View logs for a specific service:
docker compose logs app
Monitoring logs should become part of your daily workflow.
Managing Dependencies
One of Docker's biggest advantages is dependency consistency.
Without Docker:
Developer A → Node.js 20
Developer B → Node.js 18
Developer C → Node.js 16
Potential problems:
- Build failures
- Runtime errors
- Inconsistent behavior
With Docker:
All Developers
│
▼
Same Container Environment
Every developer uses identical dependencies.
Running Tests Inside Containers
Tests can run inside the same environment used in production.
Example:
docker compose exec app npm test
Benefits:
- Consistent test results
- Reduced environment-specific issues
- Easier CI/CD integration
Environment Variables
Development environments often require configuration.
Example:
NODE_ENV=development
DATABASE_URL=postgres://postgres:password@database:5432/app
Reference the file:
services:
app:
env_file:
- .env
Avoid hardcoding configuration values whenever possible.
Working with Multiple Services
Many applications require multiple containers.
Example:
Frontend
│
▼
Backend API
│
▼
Database
Docker Compose allows the entire stack to be started with a single command:
docker compose up
This significantly simplifies development.
Common Development Commands
Start the application:
docker compose up
Run in background:
docker compose up -d
View running services:
docker compose ps
Open a shell:
docker compose exec app sh
View logs:
docker compose logs -f
Stop everything:
docker compose down
Rebuild images:
docker compose build
Recommended Project Structure
A typical project may look like:
project/
├── compose.yaml
├── Dockerfile
├── .dockerignore
├── .env
├── src/
├── tests/
└── README.md
Keeping Docker-related files organized makes projects easier to maintain.
Best Practices
Use Docker Compose
Compose should be the default choice for local development.
Keep Containers Lightweight
Avoid installing unnecessary tools and packages.
Rebuild Only When Necessary
Use bind mounts for source code changes.
Rebuild only when dependencies or Dockerfiles change.
Store Configuration Outside Code
Use:
- Environment variables
.envfiles- Secret management solutions
Match Production When Possible
The closer development resembles production, the fewer surprises you'll encounter during deployment.
Summary
Docker improves the development experience by creating consistent, reproducible environments.
A good Docker development workflow includes:
- Docker Compose
- Bind mounts
- Environment variables
- Live reloading
- Container-based testing
- Proper image rebuilding strategies
These practices help teams develop, test, and deploy applications more reliably and efficiently.