Beginner's guide to automating your work.
I don't think of myself as an automation person. Im not one of those people that take a day automating something that they do like 2 minutes per day. I don't have those fancy dotfiles people have on their github. What I have is a folder of small, unglamorous bash scripts that accumulated one irritation at a time.
I do appreciate people that automate the shit out of their lives tho. But for me I try to take a simple approach for knowing what to automate and what to not. If I do something recurringly, that's a sign. Then if that takes me more than 5 minutes to do, thats a major nudge, but then if automating it will take me a short time, or i can vibe it in a couple of prompts, then I've found a good target.
The clearest offender was the way I started every single workday.
The 10-minute morning
On paper, getting ready for work shouldn't take ten minutes when you already have a Compose file. You run one command, the containers spin up, and you're good to go.
In reality, Compose always forced an annoying compromise: run it attached, and you're staring at a jumbled up mess where every service dumps its logs into the same terminal pane; run it detached, and you're flying blind, unable to see each service's logs separately unless you manually hunt down and tail individual containers.
And Compose only handled the infra anyway. I'd still have to open extra terminal tabs, cd into the repo, boot the app's dev server, manually open my coding agent of choice in the right directory, and split panes by hand to monitor what was happening.
None of these steps were hard. But together, they turned every morning into 10 minutes of window-shuffling and boilerplate rituals before I could write a single line of code.
So the services each got a tiny script, all built to the same shape:
#!/bin/bash
set -e
CONTAINER="my-service"
PORT=1234
if [ "$(docker ps -q -f name=^${CONTAINER}$)" ]; then
echo "✔ Already running"
elif [ "$(docker ps -aq -f name=^${CONTAINER}$)" ]; then
docker start $CONTAINER
else
docker run -d --name $CONTAINER -p $PORT:1234 -v my-data:/data some/image:tag
fi
echo "✅ my-service: localhost:$PORT"
It's idempotent as well. which means it's safe to run multiple times. It ends by telling me where the thing lives, so I stop hunting through notes for which port I picked. Each one got a matching stop- twin, because being able to tear everything down cleanly is what makes you willing to experiment.
Then one script per project stacks them into a full workspace using a terminal multiplexer:
SESSION="myproject"
# Already up? Just attach and get out of the way.
if tmux has-session -t "$SESSION" 2>/dev/null; then
tmux attach -t "$SESSION"; exit 0
fi
# Window 0: app processes. Window 1: my assistant, already in the repo.
# Window 2: infra, one pane per service so I can see every log.
tmux new-session -d -s "$SESSION" -c "$PROJECT_DIR"
tmux send-keys "pnpm start:dev" C-m
# ...
tmux attach -t "$SESSION"
Now two letters bring up everything, in the identical layout, every time.
The deploy I stopped doing by hand
This next one is less cozy and more consequential.
Shipping to our servers used to mean connecting to a box and driving it manually. You'd log into the container registry, pull the new images, recreate the services, then we'd also have to worry about cleaning up disk space held by old images.
Now it's one script, invoked remotely by our automation with a single argument:
#!/bin/bash
set -e
if [ -z "$1" ]; then
echo "[ERROR] IMAGE_TAG is required"
echo "Usage: ./deploy.sh <image-tag>"
exit 1
fi
IMAGE_TAG=$1
export IMAGE_TAG
echo "[DEPLOY] Deploying tag: $IMAGE_TAG"
aws ecr get-login-password --region "$AWS_REGION" \
| docker login --username AWS --password-stdin "$ECR_REGISTRY"
cd "$DEPLOY_DIR" || exit 1
docker-compose pull
docker-compose up -d --force-recreate --remove-orphans
# --- Cleanup: never let a failure here kill a deploy that already succeeded ---
set +e
docker container prune -f
for REPO in api admin worker jobs; do
docker images "$ECR_REGISTRY/$REPO" --format '{{.Tag}} {{.ID}}' \
| awk -v tag="$IMAGE_TAG" '$1 != tag { print $2 }' | sort -u \
| while read -r ID; do
docker rmi -f "$ID" || echo "[WARN] $ID still referenced — skipping"
done
done
docker image prune -af
set -e
# --- End cleanup ---
df -h /var/lib/docker
echo "[DEPLOY] Deployment successful — active tag: $IMAGE_TAG"
so where's the payoff?
I can't tell you how many hours this saved. I never measured, but i'm pretty sure its less than what it seems. But the whole point is that accumulating these types of small stuff removes that friction that would have bugged you otherwise. In my opinion, speed and the time you save isnt the big payoff of automating something, but its the energy and attention its saves you so you can focus on the more important things.
Comments · 0
Be the first to comment.