Skip to content

Blog

Notes on building and running web apps

Practical write-ups on web development, DevOps and Linux administration, from real projects.

Linux Server Backup: Files, MySQL, Postgres, MongoDB
Linux System AdminFeb 14, 2024

Linux Server Backup: Files, MySQL, Postgres, MongoDB

Back up a Linux server by dumping each database, archiving /etc and app files, running it nightly with a systemd timer, and copying it off-site with restic. I first published a backup script here in 2024. It worked, but it had the problems I now fix on client servers every month: passwords hard-coded in the script and visible in ps , backups stored on the same disk as the data, and no restore test. This is the rewritten version I use today. It covers files, MySQL or MariaDB, PostgreSQL and MongoDB, keeps credentials out of the script, ships everything off the server, and tells you when a night fails. Key takeaways Follow 3-2-1: three copies of your data, on two kinds of storage, one of them off the server. Dump databases, don't copy their files. Use mysqldump --single-transaction , pg_dump -Fc and mongodump --archive --gzip for consistent backups of a running database. Keep passwords out of scripts : use /root/.my.cnf , run Postgres tools as the postgres user, and lock files to mode 600. Schedule with a systemd timer and get alerted on failure, not just on success. Test a restore every month. Until you've restored it, you don't have a backup. What should you back up on a Linux server? Not the whole disk. You can reinstall Ubuntu and packages in minutes; what you can't recreate is your data and configuration. On a typical web server that means: Databases: every MySQL/MariaDB, PostgreSQL or MongoDB database, plus Postgres roles. Configuration: /etc (Nginx, systemd units, cron, SSH, firewall rules). Application data: uploads, .env files and anything under /srv or /var/www that isn't in git. TLS and keys if you can't simply reissue them: /etc/letsencrypt . A package list ( dpkg --get-selections ) so you can rebuild the same server. The 3-2-1 rule, in practice The script below writes local dumps to /var/backups/server on the server (copy one, fast to restore), then restic sends an encrypted, deduplicated copy to another machine or S3-compatible storage (copy two, off the server). Your live data is the third. If the server dies, is deleted by your provider, or gets ransomware, the off-site copy survives. Data is dumped to a local folder for fast restores, then copied encrypted off the server. The last step, a restore test, is the one that proves the backup is real. Step 1: Store database credentials safely The old version of this script had mysql_password="..." in it and passed it with -p , which shows up in the process list for any user on the box. Instead, put MySQL credentials in a file only root can read: sudo tee /root/.my.cnf >/dev/null <<'EOF' [client] user=backup password=use-a-long-random-password EOF sudo chmod 600 /root/.my.cnf Create a dedicated MySQL user with only the rights a dump needs: CREATE USER 'backup'@'localhost' IDENTIFIED BY 'use-a-long-random-password'; GRANT SELECT, SHOW VIEW, TRIGGER, EVENT, LOCK TABLES, PROCESS, RELOAD ON *.* TO 'backup'@'localhost'; PostgreSQL needs no password at all: run the dump tools as the postgres system user, which uses peer authentication on Ubuntu by default. Step 2: The backup script Save this as /usr/local/bin/server-backup . Delete the sections for databases you don't run. Each database is dumped to its own file, so you can restore one without touching the others. #!/usr/bin/env bash # Nightly server backup: config, app files and databases -> /var/backups/server set -euo pipefail umask 077 cd / # sudo -u postgres can't read root's cwd DEST=/var/backups/server STAMP=$(date +%F) KEEP_DAYS=7 PATHS=(/etc /srv /var/www) mkdir -p "$DEST"/{files,mysql,pg,mongo} log() { echo "[$(date +%T)] $*"; } log "files" tar -czf "$DEST/files/files-$STAMP.tar.gz" --ignore-failed-read "${PATHS[@]}" 2>/dev/null || [[ $? -eq 1 ]] dpkg --get-selections >"$DEST/files/packages-$STAMP.txt" if command -v mysqldump >/dev/null; then for db in $(mysql -NBe 'SHOW DATABASES' | grep -Ev '^(information_schema|performance_schema|sys)$'); do log "mysql $db" mysqldump --single-transaction --routines --triggers --events "$db" | gzip >"$DEST/mysql/$db-$STAMP.sql.gz" done fi if command -v pg_dump >/dev/null; then sudo -u postgres pg_dumpall --globals-only | gzip >"$DEST/pg/globals-$STAMP.sql.gz" for db in $(sudo -u postgres psql -Atc "SELECT datname FROM pg_database WHERE NOT datistemplate"); do log "pg $db" sudo -u postgres pg_dump -Fc "$db" >"$DEST/pg/$db-$STAMP.dump" done fi if command -v mongodump >/dev/null; then log "mongo" mongodump --quiet --archive="$DEST/mongo/mongo-$STAMP.archive.gz" --gzip fi find "$DEST" -type f -mtime +"$KEEP_DAYS" -delete log "done: $(du -sh "$DEST" | cut -f1)" sudo chmod 700 /usr/local/bin/server-backup sudo /usr/local/bin/server-backup A few details matter here. set -euo pipefail makes the script stop on the first failure, including a failed mysqldump inside a pipe, which the old version silently ignored. umask 077 keeps dumps readable only by root. --single-transaction gives a consistent snapshot of InnoDB tables without locking your site. On MariaDB the tools are also available as mariadb-dump and mariadb . The tar line tolerates files that change while being read (exit code 1) but still fails on real errors. Step 3: Run it nightly with a systemd timer Cron works, but a systemd timer logs every run to the journal, catches up after the server was off, and makes failure alerts easy. # /etc/systemd/system/server-backup.service [Unit] Description=Nightly server backup [Service] Type=oneshot Environment=HOME=/root ExecStart=/usr/local/bin/server-backup ExecStartPost=/usr/local/bin/offsite-backup Nice=10 IOSchedulingClass=idle # /etc/systemd/system/server-backup.timer [Unit] Description=Run server backup nightly [Timer] OnCalendar=*-*-* 02:00:00 RandomizedDelaySec=15m Persistent=true [Install] WantedBy=timers.target sudo systemctl daemon-reload sudo systemctl enable --now server-backup.timer systemctl list-timers server-backup.timer journalctl -u server-backup.service -n 50 Step 4: Copy it off the server with restic A backup on the same disk dies with the disk. restic encrypts, deduplicates and versions your backups, and it can write to another server over SFTP or to any S3-compatible bucket. Install it with sudo apt install restic , then create a password file and the repository once: sudo sh -c 'openssl rand -base64 32 > /root/.restic-pass && chmod 600 /root/.restic-pass' export RESTIC_REPOSITORY=sftp:backup@backup-host:/srv/restic/web-01 export RESTIC_PASSWORD_FILE=/root/.restic-pass sudo -E restic init Store a copy of that password somewhere else (a password manager). Without it the backups can't be decrypted, by you or anyone. Then save the off-site step as /usr/local/bin/offsite-backup : #!/usr/bin/env bash set -euo pipefail export RESTIC_REPOSITORY=sftp:backup@backup-host:/srv/restic/web-01 export RESTIC_PASSWORD_FILE=/root/.restic-pass restic backup /var/backups/server --tag nightly restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune restic check --read-data-subset=5% If you use object storage, set RESTIC_REPOSITORY=s3:https://endpoint/bucket with AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY . I compared self-hosted S3 options in MinIO is archived: self-hosted S3 alternatives . Give the backup credentials write access only to that one bucket. Step 5: Get alerted when a backup fails Silent failures are how teams discover, during an outage, that the last good backup is from March. Add an OnFailure= unit that emails or posts to Slack, or use a "dead man's switch" monitor: the script pings a URL on success, and the service alerts you when the ping doesn't arrive. Add one line at the end of offsite-backup : curl -fsS -m 10 --retry 3 https://your-monitor.example/ping/BACKUP-CHECK-ID >/dev/null Step 6: Test a restore (monthly) Pick a date in your calendar. Restore the latest snapshot into a temporary folder and load one database into a scratch instance: sudo -E restic restore latest --target /tmp/restore ls -lh /tmp/restore/var/backups/server/pg/ # PostgreSQL: restore into a new database and count rows sudo -u postgres createdb restore_test sudo -u postgres pg_restore -d restore_test /tmp/restore/var/backups/server/pg/app-2026-10-06.dump sudo -u postgres psql -d restore_test -c 'SELECT count(*) FROM "User";' # MySQL zcat /tmp/restore/var/backups/server/mysql/shop-2026-10-06.sql.gz | mysql shop_restore_test # MongoDB mongorestore --archive=/tmp/restore/var/backups/server/mongo/mongo-2026-10-06.archive.gz --gzip --dryRun Time the restore and write the number down. That's your real recovery time, and it's what you tell a client when they ask how long an outage would last. Common backup mistakes Copying /var/lib/mysql while MySQL runs. The files are inconsistent; use a dump or a filesystem snapshot. Only provider snapshots. They're useful, but they live in the same account. If the account is locked or compromised, they go too. Off-site storage the server can delete. Ransomware with root can wipe it. Use append-only credentials or object lock where available. Backing up before deploys only by hand. Automate it. My deploy script runs pg_dump -Fc before every migration; see CI/CD with automatic rollback . Backups are one part of a safe server. My Ubuntu 24.04 hardening checklist covers SSH, firewalls and updates, and the Bash scripts for DevOps post has more of the scripts I run on every server. Frequently asked questions How often should I back up a Linux server? Nightly is right for most websites and small business apps. If losing a day of orders or sign-ups is unacceptable, add PostgreSQL WAL archiving or MySQL binary logs for point-in-time recovery, or run dumps every few hours. Is rsync a backup? Not on its own. rsync mirrors the current state, so a deleted or encrypted file is mirrored too. Tools like restic or Borg keep versions, so you can go back to last Tuesday. Should I use cron or a systemd timer? Either works. A systemd timer gives you logs in the journal, runs missed jobs after downtime with Persistent=true , and can trigger an alert unit on failure, so I use timers on new servers. Where should I store off-site backups? Somewhere with a different provider or account from the server: a second VPS, a storage box, or S3-compatible object storage. Keep the encryption password in a password manager, not only on the server. How long should I keep backups? A common policy is 7 daily, 4 weekly and 6 monthly snapshots. Legal or contract requirements may need longer. Deduplication in restic keeps the storage cost low. Need backups set up properly? I set up automated, encrypted, off-site backups for Linux servers, with alerts and a documented restore test, and fix the ones that quietly stopped working. See my Linux system admin services or ask for a backup review .

Read article →
Bash Scripting for DevOps: 8 Scripts I Use on Servers
DevOpsFeb 6, 2024

Bash Scripting for DevOps: 8 Scripts I Use on Servers

Bash scripting for DevOps means small, strict scripts that automate deploys, checks and alerts. Start each with set -euo pipefail and lint it with shellcheck. When I first wrote about shell scripting here, I showed "Hello, World" and a loop that counts to five. That's how everyone learns, but it isn't what Bash is used for on a real server. This rewrite shows the scripts I actually run in production, including pieces of the deploy script that ships this website on every push. Each one is short, does one job, and fails loudly instead of silently. Key takeaways Use strict mode: set -euo pipefail stops a script at the first error, unset variable or failed pipe. Quote every variable ( "$file" , not $file ), or file names with spaces will break things. Lock jobs that must not overlap with flock , and swap releases atomically with ln -sfn + mv -T . Run shellcheck on every script; it catches most Bash bugs before they reach a server. Switch to Python when a script needs JSON parsing, complex data or grows past a couple of hundred lines. Why Bash still matters in DevOps Every Linux server already has Bash. There's nothing to install, and it's the native language of the tools you glue together: git , curl , systemctl , pg_dump , rsync . GitHub Actions steps, Dockerfile RUN lines, cron jobs and systemd units all end up running shell commands. Knowing how to write a safe script is a core DevOps skill, even on teams that use Terraform and Kubernetes. 1. A safe script template Every script I write starts like this: #!/usr/bin/env bash set -euo pipefail trap 'echo "[$(date +%T)] failed at line $LINENO (exit $?)" >&2' ERR log() { echo "[$(date +%T)] $*"; } die() { echo "error: $*" >&2; exit 1; } [[ $EUID -eq 0 ]] || die "run as root" TARGET=${1:?usage: $0 <target>} log "starting for $TARGET" -e exits when a command fails. -u treats unset variables as errors, so a typo in $BACKUP_DRI doesn't become rm -rf / . -o pipefail makes cmd | gzip fail when cmd fails. The ERR trap prints the line that failed, which turns a mystery into a one-minute fix. ${1:?message} stops with a usage message when an argument is missing. With strict mode, a failed pg_dump inside a pipe stops the script at line 3. Without it, the script would carry on, delete the old backups and print "done" over an empty file. 2. Website health check with retries This is the check my deploy script runs after every release. It waits up to a minute for the app to answer, then fails so the deploy can roll back: #!/usr/bin/env bash set -euo pipefail URL=${1:?usage: healthcheck URL} for _ in $(seq 30); do curl -fs -o /dev/null --max-time 5 "$URL" && { echo "up: $URL"; exit 0; } sleep 2 done echo "down: $URL" >&2 exit 1 curl -f turns HTTP 4xx and 5xx responses into a non-zero exit code, which is what makes it usable in scripts. The full deploy flow is in GitHub Actions CI/CD with automatic rollback . 3. Disk space alert Full disks are one of the most common causes of outages I'm called in to fix: logs or Docker images fill the root partition and the database stops writing. #!/usr/bin/env bash set -euo pipefail LIMIT=${1:-85} df -P -x tmpfs -x devtmpfs -x overlay -x squashfs -x efivarfs | awk 'NR>1 {print $5, $6}' | while read -r used mount; do pct=${used%\%} if (( pct >= LIMIT )); then echo "$(hostname): $mount is ${pct}% full" | logger -t disk-alert -s fi done Run it every 15 minutes with cron or a systemd timer, and point the output to email, Slack or your monitoring tool. To find what's eating the space: sudo du -xh / --max-depth=2 | sort -h | tail -20 , journalctl --disk-usage and docker system df . 4. SSL certificate expiry check Certbot renews automatically, until a DNS change or a broken Nginx config makes renewal fail quietly. This warns you two weeks ahead: #!/usr/bin/env bash set -euo pipefail DAYS=14 for host in "$@"; do end=$(echo | openssl s_client -connect "$host:443" -servername "$host" 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2) left=$(( ( $(date -d "$end" +%s) - $(date +%s) ) / 86400 )) if (( left < DAYS )); then echo "WARN $host expires in $left days ($end)" else echo "ok $host $left days" fi done ./ssl-check www.example.com api.example.com 5. Prevent overlapping runs with flock If a cron job takes longer than its interval, two copies run at once and fight over the same files. My deploy script uses flock so two pushes in a row never build at the same time: exec 9>/var/lock/deploy.lock flock 9 # wait for the other run to finish # flock -n 9 || exit 0 # or: skip this run if one is already going The lock is released automatically when the script exits, even if it crashes. 6. Atomic release switch with symlinks Deploying by overwriting files in place means visitors hit a half-updated app. Instead, build each release in its own folder and point a current symlink at it. Swapping the link is a single rename, so it's atomic: switch_to() { ln -sfn "$1" "$BASE/current.new" mv -T "$BASE/current.new" "$BASE/current" } PREV=$(readlink -e "$BASE/current" || true) switch_to "$BASE/releases/$RELEASE" healthcheck http://127.0.0.1:3000/ || { switch_to "$PREV"; exit 1; } Rollback is the same function pointed at the previous folder. This pattern is the heart of zero-downtime deploys in my Next.js on a VPS guide . 7. Keep only the newest N files Releases, logs and backups pile up until the disk is full. This keeps the newest five release folders and deletes the rest: ls -1d "$BASE"/releases/*/ | sort -r | tail -n +6 | xargs -r rm -rf It works because the folder names start with a UTC timestamp ( 20261006093000-a1b2c3d ), so sorting by name is sorting by date. xargs -r does nothing when there's nothing to delete. For date-based cleanup, use find "$DIR" -type f -mtime +7 -delete , as in my Linux backup script . 8. Find what's causing 5xx errors in Nginx logs When a client says "the site is throwing errors", this is usually the first command I run: #!/usr/bin/env bash set -euo pipefail LOG=${1:-/var/log/nginx/access.log} echo "== 5xx by status" awk '$9 ~ /^5/ {print $9}' "$LOG" | sort | uniq -c | sort -rn echo "== top 10 failing URLs" awk '$9 ~ /^5/ {print $7}' "$LOG" | sort | uniq -c | sort -rn | head -10 echo "== last 5" awk '$9 ~ /^5/' "$LOG" | tail -5 It assumes Nginx's default combined log format, where field 9 is the status and field 7 the path. A 502 means Nginx couldn't reach your app (it crashed or is listening on a different port); a 504 means the app was too slow to answer. Lint every script with shellcheck shellcheck finds unquoted variables, useless cat s, broken conditions and dozens of other mistakes. Install it with sudo apt install shellcheck and run it in CI so bad scripts never get merged: shellcheck scripts/*.sh bash -n scripts/deploy.sh # syntax check only When not to use Bash Bash is great for gluing commands together, and painful for everything else. I switch to Python or TypeScript when a script needs to parse JSON beyond a quick jq call, handle complex data structures, call APIs with retries and pagination, or when it passes about 200 lines. Configuration of many servers belongs in Ansible or similar, not in a loop over SSH. For managing users and permissions, see my Linux user management script , and for the server baseline these scripts run on, the Ubuntu 24.04 hardening checklist . Frequently asked questions Is Bash scripting still useful for DevOps in 2026? Yes. CI pipelines, Dockerfiles, cron jobs and deploy hooks all run shell commands, and most server troubleshooting happens in a shell. Bash is the glue even when Terraform or Kubernetes manage the bigger picture. What does set -euo pipefail do? It makes Bash stop on the first failed command ( -e ), treat unset variables as errors ( -u ), and fail a pipeline when any command in it fails ( -o pipefail ). Together they stop a broken script before it does damage. Should I use #!/bin/bash or #!/usr/bin/env bash? Both work on Linux. #!/usr/bin/env bash finds Bash on the PATH , which is more portable to macOS and BSD. Use #!/bin/sh only if you write strictly POSIX shell without Bash features. How do I run a Bash script on a schedule? Use cron ( crontab -e ) for simple jobs, or a systemd service plus timer for logging, retries and failure alerts. Always use absolute paths in scheduled scripts, since the environment is minimal. What's the best way to learn shell scripting for DevOps? Automate one real task you do by hand, such as a deploy, a backup or a log check. Run shellcheck on it, read its warnings, and keep improving the script as it breaks. Real problems teach faster than exercises. Need servers automated? I write deploy scripts, health checks, backups and alerting for Linux servers, and set up CI/CD so they run on every push. See my DevOps services or send me the task you'd like automated .

Read article →
What Is DevOps? A Practical Guide for Small Teams
DevOpsFeb 1, 2024

What Is DevOps? A Practical Guide for Small Teams

DevOps is how a team ships software often and safely: automated tests, one-click deploys, monitoring and fast rollback, owned by the people who build it. Most explanations of DevOps stop at "culture" and "collaboration". That's true, but it doesn't help a founder with one server and a deadline, or a small team where deploys mean SSH, git pull and hoping. I set up and run DevOps for small teams and for my own products. This site deploys itself on every push to main , with a database backup, a health check and an automatic rollback. This guide covers what DevOps means in practice, what to set up first, and when it's worth bringing in help. Key takeaways DevOps is a way of working, not a job title or a tool. The same team builds, ships and runs the software, and automates the boring parts. The core loop is short: commit, automated tests, automated deploy, health check, monitoring, learn, repeat. Start with four things: a CI pipeline, a scripted deploy with rollback, backups you've restored at least once, and uptime alerts. Measure it with the four DORA metrics: deployment frequency, lead time for changes, change failure rate and time to recover. Small teams don't need Kubernetes. One well-run VPS with a good pipeline beats a cluster nobody understands. What does DevOps mean in plain English? Before DevOps, developers wrote code and "threw it over the wall" to an operations team that deployed and ran it. When something broke, each side blamed the other, releases were rare and scary, and fixing a bug in production could take weeks. DevOps removes the wall. The people who write a feature are responsible for getting it to production and keeping it healthy there. To make that possible without burning out, they automate everything repeatable: testing, building, deploying, provisioning servers, backups and alerts. The result is small changes shipped often, each one easy to check and easy to undo. So when someone asks "what is DevOps?", the honest short answer is: automation plus ownership . Tools like GitHub Actions, Docker, Terraform or Ansible are how you do it, not what it is. What does a DevOps pipeline look like? Here's the real pipeline behind this website, which is a Next.js front end and a NestJS API on one Ubuntu server: Push to main . GitHub Actions starts a job. CI checks. Install dependencies with a frozen lockfile, type-check, run tests, build. A failure stops here and production is untouched. Deploy over SSH. The Actions key can only run one deploy command, nothing else. Build a new release folder while the old version keeps serving traffic. Back up the database, run migrations, swap a symlink, restart the process. Health check. If /health doesn't answer within about 90 seconds, the symlink is swapped back to the previous release automatically. Every change takes the same automated path. A broken test stops it before production; a failed health check after deploy puts the previous release back without anyone logging in. None of this needs a big platform. It's a GitHub Actions workflow and a Bash deploy script of about 150 lines. I wrote up the full setup in GitHub Actions CI/CD to a VPS with automatic rollback . The DevOps practices that matter most Continuous integration (CI) Every push is built and tested automatically on a clean machine. "Works on my laptop" stops being an argument, and broken code is caught minutes after it's written instead of days later. Continuous delivery and deployment (CD) Deploying is one command or one merge, and it's the same every time. When deploys are boring, you do them more often, so each one is smaller and safer. The opposite, a big monthly release, is where most outages come from. Infrastructure as code (IaC) Servers, DNS, firewalls and databases are described in files (Terraform, Ansible, Docker Compose, or even a well-commented shell script) and kept in git. You can rebuild a server from scratch, review infrastructure changes like code, and see who changed what. Monitoring, logging and alerts You should hear about a problem from an alert, not from a customer. At minimum: uptime checks from outside, disk and memory alerts, error logs you can search, and SSL expiry warnings. Backups and recovery Backups are part of DevOps, not an afterthought. My deploy script dumps the database before every migration, and nightly backups go off the server. The step most teams skip is testing a restore. My Linux server backup guide covers it. Security built in Hardened SSH, a firewall, automatic security updates, secrets outside the repo and dependency checks in CI. My Ubuntu 24.04 hardening checklist is the baseline I apply to every server. How do you measure DevOps? The DORA metrics Google's DORA research program has tracked thousands of teams for years and settled on four metrics that predict delivery performance: Deployment frequency: how often you ship to production. Lead time for changes: how long from commit to running in production. Change failure rate: what share of deploys cause a problem that needs a fix or rollback. Time to restore service: how fast you recover when a deploy goes wrong. You don't need a dashboard to start. Write the four numbers down today, and check them again after a month of changes. If deploys went from "every two weeks, takes an afternoon" to "several times a day, takes five minutes", it's working. What should a small team set up first? If you're starting from manual deploys, this is the order I'd follow. Each step pays for itself before the next one: Put everything in git , including server config and deploy scripts. No secrets in the repo. Add CI that installs, type-checks, tests and builds on every push. Script the deploy with release folders and a health check, so rollback is one command (then make it automatic). Set up backups that leave the server, and restore one to prove it works. Add monitoring: an external uptime check and alerts for disk, memory and certificate expiry. Harden the server and keep it patched. Only then consider containers, staging environments or infrastructure as code with Terraform. Skipping straight to Kubernetes is the most common mistake I see. It solves problems small teams don't have yet and adds a lot of moving parts. A single VPS also costs far less than most managed platforms, as I worked out in Vercel vs self-hosting costs . Is DevOps a role or a culture? Both, and the confusion is fair. The original idea is cultural: shared ownership between development and operations. In practice, companies also hire "DevOps engineers" to build the pipelines, infrastructure and monitoring that make that ownership possible. For a small company, that's often a freelancer who sets everything up, documents it, and hands it over so the developers can run it day to day. Signs you need DevOps help Deploys are manual, and only one person knows how to do them. You've had an outage and couldn't tell what changed. You don't know when your last working backup was taken. The server hasn't been updated in months, or you're not sure who has SSH access. Your hosting bill keeps growing and nobody knows why. Frequently asked questions What is DevOps in simple words? DevOps is a way of building software where the same team develops, deploys and runs it, and automates testing, deployment and monitoring. The goal is to release small changes often, with less risk. What tools are used in DevOps? Common ones are Git and GitHub Actions or GitLab CI for pipelines, Docker for packaging, Terraform or Ansible for infrastructure as code, Nginx, and Prometheus, Grafana or an uptime service for monitoring. The tools matter less than automating the same path every time. Does a small business need DevOps? Yes, at a small scale. A CI pipeline, a scripted deploy with rollback, tested backups and uptime alerts take a few days to set up and prevent the outages and lost data that hurt small businesses most. Is DevOps the same as CI/CD? No. CI/CD is one practice inside DevOps. DevOps also covers infrastructure as code, monitoring, incident response, security and the shared ownership that ties them together. How long does it take to set up a DevOps pipeline? For a typical web app on a VPS, a working CI pipeline with automated deploys and rollback takes a few days. Monitoring, backups and hardening add a few more. Larger systems with several services take longer. Want this set up for your project? I build CI/CD pipelines, deploy scripts with automatic rollback, backups and monitoring for small teams, and document everything so you can run it yourself. See my DevOps services or tell me about your setup .

Read article →
My New Work Station: Embracing the Heights of Productivity with a Standing Desk
Web DevelopmentJan 30, 2024

My New Work Station: Embracing the Heights of Productivity with a Standing Desk

Introduction: Welcome to the unveiling of my revamped workspace! In the pursuit of a healthier and more productive work environment, I recently made a significant upgrade – I embraced the world of standing desks. Join me as I share my journey into the realm of ergonomic bliss and discover how this change has positively impacted my daily work routine. The Decision to Stand: For many of us, the sedentary nature of desk jobs can take a toll on both physical and mental well-being. The idea of a standing desk began to intrigue me as I sought a solution to combat the notorious slouch and fatigue that often accompany long hours at a traditional desk. The decision to stand while working not only promised increased energy levels but also hinted at a healthier posture and improved focus. Setting Up the Standing Desk: The excitement of receiving and assembling my new standing desk was palpable. As each piece came together, I envisioned a workspace that would not only elevate my computer but also lift my productivity to new heights. The adjustable features of the desk allowed me to find the perfect height, ensuring a comfortable and customized experience. The Impact on Productivity: The transition to a standing desk brought about a noticeable shift in my daily work routine. While the physical benefits were evident – reduced back pain, increased circulation, and improved posture – the impact on productivity was equally profound. Standing while working encouraged better concentration, minimized distractions, and infused a renewed sense of alertness into my tasks. Ergonomics and Comfort: One of my initial concerns was whether standing for extended periods would lead to discomfort. However, with the right ergonomic setup, including an anti-fatigue mat and proper footwear, my fears were quickly dispelled. The standing desk's adaptability allowed me to seamlessly switch between sitting and standing, striking the perfect balance between comfort and health. Tips and Tricks for Standing Desk Success: In my exploration of the standing desk lifestyle, I've gathered some invaluable tips and tricks. From incorporating short breaks to stretch and move around to investing in a supportive anti-fatigue mat, these insights have contributed to making the standing desk experience not just beneficial but enjoyable. The Standing Desk Chronicles: Follow me on this blog series, 'The Standing Desk Chronicles,' where I'll share updates on my journey, delve into the latest ergonomic trends, and explore the broader landscape of creating a workspace that nurtures both productivity and well-being. Conclusion: My new workstation – the standing desk – has truly been a game-changer. It's not just about standing; it's about elevating my work experience, quite literally. Join me in this ongoing adventure as we explore the heights of productivity, one standing desk at a time. Welcome to 'My New Work Station: The Standing Desk Chronicles.' Stand tall, work smart!

Read article →