How I turned a repetitive Ubuntu setup process into reusable automation, and what several unexpected infrastructure problems taught me along the way.

Contents

Introduction

Every developer eventually develops a personal workflow. Mine usually begins the same way whenever I create a new Ubuntu environment, whether it is a cloud server, a virtual machine or a fresh WSL installation.

Install Git. Configure Python with pyenv. Install Docker, Visual Studio Code, DBeaver and Google Chrome. Adjust a few shell settings. Then verify that everything works together. None of these tasks is particularly difficult on its own. After repeating the same process many times, though, I started asking myself a different question:

Why was I still doing it manually at all?

I enjoy automating repetitive work. The time saved is useful, but that has never been the main reason for me. What matters more is creating a process that is predictable, repeatable and easy to maintain. A development environment should behave consistently whether I am setting up a new laptop, preparing an AWS EC2 instance, creating a virtual machine or rebuilding WSL from scratch.

That idea became automate-dev-setup.

The repository started as a small personal project for automating my own Ubuntu development environment. Over time, new requirements and unexpected problems made it more robust. Eventually, it became useful enough that my DevOps team also adopted it as part of our internal setup process for Ubuntu development machines.

This article is not intended to be another installation guide. The repository already contains the instructions needed to use the automation. Instead, I want to show why I built it, how I structured it, which problems made the project more interesting than I originally expected, and what I learned from solving them.

Why I Built This

One principy has guided much of my engineering work over the years:

If I find myself solving the same problem repeatedly, I should first ask whether the problem should exist at all.

That does not always mean writing software. Sometimes better documentation is enough. Sometimes the workflow itself should be simplified. In this case, automation was the natural answer.

The goal was not simply to install software faster. I wanted a development environment that I could rebuild from a clean Ubuntu installation without relying on memory, a personal checklist or a collection of undocumented workarounds.

What the Project Does

At its simplest, the project allows me to prepare a complete Ubuntu development environment using a single command

make all

The automation installs and configures the tools I use regularly:

Git
Python (managed through pyenv)
Docker Engine
Visual Studio Code
DBeaver
Google Chrome

Each tool also has its own installation target, so I can install only what I need. Rather than hiding the setup behind an opaque installer, the individual steps are implemented as Bash scripts and use official installation sources whenever possible. The repository therefore serves two purposes at the same time: automation, because the environment can be reproduced automatically and executable documentation, because the exact setup process remains visible in the repository.

A Quick Example

TStarting from a fresh Ubuntu machine requires only a few steps:

git clone https://github.com/lien-nguyen/automate-dev-setup.git
cd automate-dev-setup

make all

(I will add a short scren recording or screenshot here showing the complete setup running on a fresh AWS EC2 Ubuntu instance)

Seeing the automation run from a completely clean machine is probadly the easiest way to understand the purpose of the project.

Designing for Reusability

Although this repository is relatively small, I tried to apply the same engineering principles that I use in larger software projects.

Modular scripts

Each tool has its own installation scripts.

One entry point

The Makefile provides a consistent interface.

Transparency

Every installation step is visible. Nothing is hidden behined binary installers or large shell scripts downloaed from unknow sources.

Extensibility

As my development workflow evolves, I can add new tools without redesigning the project. Automation should grow together with the engineering workflow.

The Interesting Part Wasn't the Automation

Writing installation scripts turned out to be the easy part. The real engineering work started when I encountered problems that were difficult to diagnose. Several of these issues had misleading symptoms. For example:

  • Docker containers could not resolve DNS under WSl even though the host machine had internet access.
  • Git PPA installation failed inside a corporate VM because of SSL inspection.
  • Docker networking behaved differently because of an MTU mismatch between WSL and Docker's bridge network.

Each problem required understanding the root cause before implementing a reliable solution. Instead of simply documenting the workaround, I decided to include the reasoning in the repository so that futures users, including my future self, would understand why the solution works. In the following sections, I'll walk through each of these problems and explain how I diagnosed them.

Engineering Lessons

This project reminded me that automation is rearly about saving a few minutes. The real value comes from removing uncertainty. Every munual installation introduces opportunities for inconsistency. Every undocumented workaround becomes knowledge that eventually disappears.

Automation helps standardize the process. Documentation explains the reasoning. Together they create something that is much more valuable than a collection of installation commands.

What's Next

This repository will continue to evolve together with my development workflow. Whenever I adopt a new tool, discover a better approach, or solve another environment-specific problem, I'll update both the automation and the accomanying documentation. My goal isn't to automate everything. It's to automate the things that sould never require solving twice.

Source Code

The complete project is available on Github under MIT License. If you find it useful, have suggestions for improvement, or discover a better solution to one of the documented problems, feedback and contributions are always welcome.

Comments (0)

No comments yet. Be the first!

Leave a Comment

← DevOps