Skip to main content

Command Palette

Search for a command to run...

Step-by-Step: Bootstrapping my Containerized Environment

From zero to a running multi-service stack

Updated
6 min read
Step-by-Step: Bootstrapping my Containerized Environment
N
Senior-level Fullstack Web Developer with 10+ years experience, including 2 years of Team Lead position. Specializing in responsive design and full-stack web development across the Vue.js and .NET ecosystems. Skilled in Azure/AWS cloud infrastructure, focused on DevOps techniques such as CI/CD. Experienced in system design, especially with software architecture patterns such as microservices, BFF (backend-for-frontend). Hands-on with Agile practices in team leading, and AI-assisted coding.

Series: Containers, Actually: Building Real Local Dev Environments
ACT III — Real Implementation: My Humhub Stack
Previous: Containerized: My Repository Structure & Design Decisions
Next: Working Day-to-Day Inside my Containerized Stack

Disclaimer (NDA Notice)
This walkthrough is based on a real internal project and development stack.
Due to NDA constraints, some repository names, paths, credentials, and configuration values are anonymized or simplified.

The commands, flow, Docker behavior, and failure modes are real and representative.
Treat this article as a reliable execution guide, not a verbatim copy of the internal setup.

This is the moment where theory meets reality.

Up to now, we’ve talked about why containerized local development exists, what a modern full-stack system looks like, and how Docker, WSL, and tooling fit together. In this article, we actually build the environment—carefully, deliberately, and with explanations at every step.

The goal is not speed.
The goal is repeatability with understanding.


Step 0: Mental Preparation (Seriously)

Before running commands, make sure these assumptions are true:

  • You are on Windows

  • You are willing to use WSL 2 as your primary dev environment

  • Your code will live inside the Linux filesystem

  • Docker will run inside WSL, not against C:\

If any of those feel optional, you’ll likely hit friction later.


Step 1: Installing Prerequisites

Required Components

At a minimum, you need:

  • Docker Desktop

  • WSL 2

  • A Linux distribution (Ubuntu recommended)

  • Git

  • A terminal

  • An editor (VS Code)

These tools form a stack, not a menu. Installing only some of them leads to half-working systems.

Docker Desktop

Install Docker Desktop for Windows and ensure:

  • WSL 2 backend is enabled

  • Your Linux distro is integrated

  • Docker starts without errors

After installation, verify from inside WSL:

docker version
docker compose version

If these commands fail inside WSL, Docker is not integrated correctly.

Windows Terminal isn’t cosmetic—it makes WSL usable.

Once installed:

  • Add a profile for your Linux distro

  • Set it as default if possible

From now on, all commands in this article assume you are inside a WSL terminal, not PowerShell.


Step 2: WSL Setup (If Not Already Done)

If WSL is already installed and you’re using it daily, you can skim this section.

Verify WSL Version

wsl --list --verbose

You should see something like:

Ubuntu-22.04   Running   2

If your distro is using version 1, convert it:

wsl --set-version Ubuntu-22.04 2

WSL 1 will cause Docker pain later. Don’t negotiate with it.

Resource Configuration (.wslconfig)

WSL 2 will happily consume all available resources unless constrained.

Create or edit this file on Windows:

C:\Users\<YourUser>\.wslconfig

Example:

[wsl2]
memory=6GB
processors=4

Why this matters:

  • Prevents Docker from starving Windows

  • Keeps performance predictable

  • Avoids “my laptop sounds like a jet engine” syndrome

Restart WSL after editing:

wsl --shutdown

Step 3: Choosing the Right Location for Code

This step is non-negotiable.

Correct Location

Inside WSL:

cd ~
mkdir -p projects
cd projects

Your path should look like:

/home/youruser/projects

Incorrect Location (Do Not Use)

/mnt/c/Users/youruser/projects

Using /mnt/c introduces filesystem translation overhead and breaks Docker performance in subtle ways.


Step 4: Cloning the Repository

Clone the repository inside WSL:

git clone https://example.com/your-repo.git
cd your-repo

At this point, your directory should contain:

ls

Typical output:

docker/
app/
docker-compose.yml
.env.example
README.md

If you see Windows-style paths or permission errors, stop here and fix them.


Step 5: Environment Variables Setup

Copy the example environment file:

cp .env.example .env

Edit .env as needed:

nano .env

Typical values:

APP_ENV=local
DB_HOST=mysql
DB_NAME=app
DB_USER=app
DB_PASSWORD=secret
REDIS_HOST=redis
QUEUE_HOST=rabbitmq

Important notes:

  • .env is local-only

  • Do not commit it

  • Values should match service names in docker-compose.yml

Environment variables configure values, not architecture.


Step 6: First docker-compose build

Now we build images.

docker-compose build

What This Actually Does

  • Reads docker-compose.yml

  • Identifies services with build: directives

  • Executes Dockerfiles

  • Caches layers where possible

  • Produces local images

Nothing is running yet.

Common Failure Points (Build Phase)

  1. Network failures

    • Package downloads timing out

    • Temporary DNS issues

  2. Permission issues

    • Files owned by Windows user

    • Incorrect bind mount paths

  3. Build cache confusion

    • Old layers masking changes

If things get weird:

docker-compose build --no-cache

Use this sparingly—it’s expensive but clarifying.


Step 7: First docker-compose up -d

This is where the system comes alive.

docker-compose up -d

What This Actually Does

  • Creates Docker networks

  • Creates volumes

  • Starts containers

  • Attaches containers to networks

  • Applies environment variables

  • Exposes mapped ports

The -d flag means “detached”—containers run in the background.


Step 8: Verifying the System Is Running

Check Container Status

docker-compose ps

Expected:

  • All services listed

  • Status is Up

  • No constant restarting

Inspect Logs

For a specific service:

docker-compose logs app

Or tail logs live:

docker-compose logs -f web

Logs are the first place to look when something feels wrong.

Verify Service Health Individually

Web App

Open browser:

http://localhost:4202

You should see:

  • App landing page

  • Or framework install screen

  • Not a gateway error

MySQL

docker-compose exec mysql mysql -u app -p

If you can connect, persistence is working.

Redis

docker-compose exec redis redis-cli ping

Expected output:

PONG

Elasticsearch

curl http://localhost:9200

You should receive JSON describing the cluster.

Kibana

http://localhost:5601

Kibana takes longer to start—this is normal.

RabbitMQ Management UI

http://localhost:15672

Login credentials are usually defined in .env or compose config.


Common Failure Patterns (And What They Mean)

Containers Restarting Repeatedly

  • Misconfigured environment variables

  • Missing volumes

  • Invalid config files

Check logs immediately.

Web Loads, But App Errors

  • Database not initialized

  • Cache not reachable

  • App config mismatch

This is application-level, not Docker-level.

Slow Performance

  • Code lives under /mnt/c

  • Resource limits too low

  • Antivirus scanning Docker files

Fix filesystem placement first.


If Everything Works—Stop and Breathe

At this point:

  • You have a multi-service system running locally

  • Data persists across restarts

  • Services communicate via Docker networking

  • Your environment is reproducible

That’s not trivial. Don’t rush past it.


What Comes Next

Now that the environment boots reliably, the next step is living inside it:

  • Running app commands

  • Debugging

  • Rebuilding selectively

  • Resetting state safely

  • Understanding daily workflows

That’s what the next article covers.

Bootstrapping is not about memorizing commands.
It’s about making the system predictable—so you can trust it tomorrow.

Containers, Actually: Building Real Local Dev Environments

Part 9 of 18

This series explores full-stack local development with Docker—from core concepts and best practices to a real Windows implementation. The goal is to understand how things run, why they work, and how to build reproducible production environment.

Up next

Containerized: My Repository Structure & Design Decisions

Designing a Docker repo that explains itself