How to Automate Dev Environment Setup: A Practical Guide

Automate your dev environment setup by writing shell scripts for package installation, using Docker to containerize your runtime, and layering Ansible playbooks for team-wide configuration management. Store all setup logic in version control so every machine converges on an identical, reproducible state in minutes instead of hours.

This guide walks through each approach with copy-paste examples you can adapt immediately, from a basic bash setup script to a full docker-compose stack and an Ansible playbook.

Why Automate Your Development Environment

Manual setup processes are error-prone and difficult to reproduce. When you configure a machine by hand, you accumulate subtle differences over time—specific package versions, custom shell aliases, project-specific tools—that never get documented. Team members joining a project inherit inconsistent setups, leading to “works on my machine” bugs and wasted debugging time.

Automation solves these problems by codifying your environment as version-controlled configuration. When your setup lives in code, you can review changes through pull requests, roll back problematic updates, and apply identical configurations across any number of machines. New team members can go from zero to a fully configured development environment in minutes rather than days.

Starting Simple: Shell Scripts

The most accessible approach to environment automation uses shell scripts. Even basic bash scripts that automate package installation significantly reduce setup time and ensure consistency.

A straightforward setup script might look like this:

#!/bin/bash
set -e

# Update package lists
sudo apt update && sudo apt upgrade -y

# Install essential tools
sudo apt install -y git curl wget vim unzip zsh

# Install programming language runtimes
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

# Configure git
git config --global user.name "Your Name"
git config --global user.email "you@example.com"

# Install Oh My Zsh
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended

This script handles the initial machine configuration. Run it on a fresh Ubuntu machine and you get a consistent baseline setup in minutes. The script is version-controllable—you can track changes, branch for different OS variants, and review modifications before applying them.

For project-specific dependencies, create scripts within each repository:

#!/bin/bash
# project-setup.sh - Run from project root

# Install Node.js dependencies
npm install

# Copy environment configuration
cp .env.example .env

# Initialize pre-commit hooks
npm run prepare

echo "Project environment ready. Run 'npm run dev' to start."

Using Docker for Reproducible Environments

Docker provides stronger isolation than shell scripts by containerizing your entire development environment. This approach packages not just dependencies but the exact runtime environment, eliminating OS-level differences entirely.

A basic development Dockerfile:

FROM node:20-slim

WORKDIR /app

# Install development dependencies
RUN apt-get update && apt-get install -y \
    git \
    curl \
    && rm -rf /var/lib/apt/lists/*

# Copy package files first for better caching
COPY package*.json ./
RUN npm ci --only=production

# Copy source code
COPY . .

# Expose development server port
EXPOSE 3000

CMD ["npm", "run", "dev"]

For local development, use docker-compose to orchestrate multi-service environments:

version: '3.8'
services:
  app:
    build: .
    volumes:
      - .:/app
      - /app/node_modules
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
    depends_on:
      - postgres
      - redis

  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: developer
      POSTGRES_PASSWORD: devpass
    volumes:
      - pgdata:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  pgdata:

With this configuration, any team member runs docker-compose up and gets a complete development stack—application, database, and cache—without installing anything beyond Docker.

Configuration Management with Ansible

For more complex environments across multiple machines, Ansible provides automation at scale. Ansible uses declarative YAML files called playbooks to describe desired system states, handling the complexity of idempotent configuration automatically.

An Ansible playbook for development machine setup:

---
- name: Configure development workstation
  hosts: localhost
  become: yes
  vars:
    developer_username: developer
    programming_languages:
      - { name: nodejs, version: "20" }
      - { name: python, version: "3.11" }

  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: Install system packages
      apt:
        name:
          - git
          - curl
          - wget
          - vim
          - unzip
          - build-essential
          - docker.io
        state: present

    - name: Configure git global settings
      git_config:
        name: "{{ item.name }}"
        value: "{{ item.value }}"
        scope: global
      loop:
        - { name: "user.name", value: "Developer" }
        - { name: "user.email", value: "dev@example.com" }
        - { name: "init.defaultBranch", value: "main" }

    - name: Install pyenv for Python management
      become_user: "{{ developer_username }}"
      shell: |
        curl https://pyenv.run | bash
      args:
        creates: "/home/{{ developer_username }}/.pyenv"

    - name: Install VS Code extensions
      community.general.vscode_extension:
        executable: code
        extension_ids:
          - ms-python.python
          - esbenp.prettier-vscode
          - github.copilot

Run this playbook with ansible-playbook development.yml and Ansible ensures your machine matches the specification. The idempotent nature means running the playbook multiple times produces the same result—safe for repeated application or CI/CD pipelines.

Dotfiles: Personal Configuration Management

Beyond project-specific tools, developers accumulate personal configuration through dotfiles—hidden configuration files like .bashrc, .zshrc, .vimrc, and .gitconfig. Managing these as a dotfiles repository provides portable personal environments.

A minimal dotfiles structure:

dotfiles/
├── .gitignore
├── install.sh
├── .zshrc
├── .vimrc
├── .gitconfig
└── .config/
    └── starship.toml

The installation script symlinks files to their expected locations:

#!/bin/bash
DOTFILES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

link_file() {
  local source="$DOTFILES_DIR/$1"
  local target="$HOME/$1"
  
  if [ -e "$target" ]; then
    if [ -L "$target" ]; then
      echo "Skipping $1 (already linked)"
    else
      echo "Backing up $1"
      mv "$target" "$target.backup"
    fi
  fi
  
  mkdir -p "$(dirname "$target")"
  ln -sf "$source" "$target"
  echo "Linked $1"
}

link_file ".zshrc"
link_file ".vimrc"
link_file ".gitconfig"
link_file ".config/starship.toml"

Combine this with a shell script that installs dependencies, and you have a complete personal environment reproducible across any machine.

Automating for Teams

Team environments benefit most from automation because they multiply the effort saved across multiple developers. Consider storing setup scripts in a dedicated repository accessible to all team members. Use tools like Machete or GitHub’s Template Repositories to provide standardized starting points for new projects.

Documentation matters as much as the scripts themselves. Include README files explaining how to run setup scripts, what assumptions the automation makes about the base system, and how to troubleshoot common issues. Even the best automation fails when users don’t understand how to use it or what went wrong when something breaks.

Start with shell scripts, add Docker for project reproducibility, and layer Ansible for team-wide infrastructure management as your needs grow.

Built by theluckystrike — More at zovo.one