{"id":35751,"date":"2026-01-12T00:35:07","date_gmt":"2026-01-11T17:35:07","guid":{"rendered":"https:\/\/dps.media\/xay-dung-he-thong-docker-custom-backup-deep-space-chi-voi-next-js-dockerode\/"},"modified":"2026-01-12T00:35:07","modified_gmt":"2026-01-11T17:35:07","slug":"xay-dung-he-thong-docker-custom-backup-deep-space-chi-voi-next-js-dockerode","status":"publish","type":"post","link":"https:\/\/dps.media\/en\/xay-dung-he-thong-docker-custom-backup-deep-space-chi-voi-next-js-dockerode\/","title":{"rendered":"Building a Custom Docker Backup System \u201cDeep Space\u201d Using Only Next.js &amp; Dockerode"},"content":{"rendered":"<h2>Introduction: The Nightmare Called \u201cManual Backup\u201d<\/h2>\n<p>If you are a programmer, a System Admin or simply a science lover operating personal VPS, you must have experienced the chilling sensation when you accidentally typed the command <code data-no-translation=\"\">rm -rf<\/code> in the wrong directory, or on a fine day the Database Container suddenly dies without apparent reason.<\/p>\n<p>Managing backups for Docker Container on VPS usually revolves around the following solutions:<\/p>\n<ol>\n<li><strong>Cronjob + Shell Script:<\/strong> This is the most common way. You write a script <code data-no-translation=\"\">backup.sh<\/code>, compress the volume directory and push it to the Cloud. It sounds simple but when the number of containers reaches dozens, managing this pile of scripts is a real ordeal. Not to mention that restoring is extremely manual and prone to errors.<\/li>\n<li><strong>Using Existing Tools (Portainer, Yacht\u2026):<\/strong> These tools are great for managing Containers, but backup features are often quite basic, or require the Business version (paid) to have scheduling or advanced automatic backup features.<\/li>\n<li><strong>No backup at all:<\/strong> \u201cIt probably won't happen to me\u201d \u2013 and this is often the beginning of sad data loss stories.<\/li>\n<\/ol>\n<p>Because of those pains, I decided to build with my own hands <strong>Docker Guard<\/strong> \u2013 A professional Backup &amp; Restore system, with a sci-fi movie-like interface, and most importantly: <strong>It completely solves the safe backup problem.<\/strong><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/dps.media\/wp-content\/uploads\/mcp\/2026\/01\/docker_guard_hero_dashboard_1768152529692.jpg\" alt=\"Docker Guard Dashboard Interface\" title=\"\"><\/p>\n<h2>System Architecture &amp; Tech Stack \u201cDeep Space\u201d<\/h2>\n<p>Instead of writing a dry Desktop or CLI application, I chose to build Docker Guard as a modern Web Application. This allows me to access and manage backups from anywhere, on phone or laptop, as long as there's a browser.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/dps.media\/wp-content\/uploads\/mcp\/2026\/01\/docker_guard_architecture_1768152547358.jpg\" alt=\"Docker Guard System Architecture\" title=\"\"><\/p>\n<h3>Why Next.js?<\/h3>\n<p>Many of you might wonder: \u201cWhy use Next.js (Node.js\/React) for System management instead of Python or Go?\u201d.<\/p>\n<p>The answer lies in flexibility and development speed. With <strong>Next.js 14 (App Router)<\/strong>, I can:<\/p>\n<ul>\n<li><strong>Backend (Server Actions):<\/strong> Write Node.js code that runs directly on the Server to communicate with Docker Daemon, handle file system, zip compression\u2026 without needing to set up a separate Express\/NestJS server.<\/li>\n<li><strong>Frontend (React Server Components):<\/strong> Render interface extremely fast, good SEO (even though internal tools don't need much SEO, but fast loading is satisfying).<\/li>\n<li><strong>Real-time UI:<\/strong> Easily integrate Streaming UI to display running backup logs in real time.<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/dps.media\/wp-content\/uploads\/mcp\/2026\/01\/docker_guard_tech_stack_1768152602014.jpg\" alt=\"Docker Guard Tech Stack\" title=\"\"><\/p>\n<p>In addition, the interface is meticulously crafted with <strong>Tailwind CSS<\/strong> and <strong>Framer Motion<\/strong>. I drew inspiration from Cyberpunk\/Sci-fi interfaces with a dark color scheme (Deep Space), Glassmorphism effects (frosted glass) and Neon lighting. Server management doesn't have to be boring with black and white screens, it has to be \u201ccool\u201d to be motivated to work!<\/p>\n<h2>3 Money-Making Features of Docker Guard<\/h2>\n<h3>1. Smart Backup: Not Just Copying Data<\/h3>\n<p>Most common backup scripts only perform <code data-no-translation=\"\">tar<\/code> (compression) of volume directories. But what if you lose the entire VPS? You have the data, but you forget what <code data-no-translation=\"\">docker run<\/code> command you used, with which environment variables, which ports mapped externally?<\/p>\n<p><strong>Docker Guard solves this with the \u201cSmart Backup\u201d mechanism:<\/strong><\/p>\n<ol>\n<li><strong>Inspect Container:<\/strong> The tool will read the entire current configuration of the container (Image tag, Env vars, Ports, Network settings, Cmd\u2026).<\/li>\n<li><strong>Serialize Config:<\/strong> Save that configuration to a file <code data-no-translation=\"\">config.json<\/code>.<\/li>\n<li><strong>Compress Volumes:<\/strong> Compress all data from Volumes mounted into the container.<\/li>\n<li><strong>Package:<\/strong> Package everything into a single <code data-no-translation=\"\">.zip<\/code> file.<\/li>\n<\/ol>\n<p><img decoding=\"async\" src=\"https:\/\/dps.media\/wp-content\/uploads\/mcp\/2026\/01\/docker_smart_backup_concept_1768152567098.jpg\" alt=\"Smart Backup Process Visualization\" title=\"\"><\/p>\n<p>Thanks to this, Docker Guard's backup file is like a \u201cresurrection pill\u201d. You take this zip file to a brand new VPS, click Restore, and boom! Your container comes back to life exactly as before, with every environment variable, every image version intact.<\/p>\n<h3>2. Restore to New Container: Clone for Safe Testing<\/h3>\n<p>This is the feature I am most proud of and use the most. In a Production environment, restoring over a running container is an extremely high risk. What if the backup file is corrupted? What if the new version is incompatible with the old data?<\/p>\n<p>Docker Guard allows you to perform <strong>\u201cRestore to New Container\u201d<\/strong>. Instead of overwriting, it will:<\/p>\n<ul>\n<li>Create a new container with an example name: <code data-no-translation=\"\">my-app_restored_20231025<\/code>.<\/li>\n<li>Map to a random port (or one you choose) to avoid conflicting with the main app (Live App) port.<\/li>\n<li>Data is extracted into a separate folder.<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/dps.media\/wp-content\/uploads\/mcp\/2026\/01\/docker_restore_feature_1768152583007.jpg\" alt=\"Restore to New Container Feature\" title=\"\"><\/p>\n<p>At this point, you have 2 containers running in parallel: the Live version and the Restore version. You can enter the Restore version to check, test if the data is okay, if the code runs stably. When everything is 100% certain, then you switch traffic over or replace the old one. This is the process <strong>Zero Downtime Recovery<\/strong> that large systems often use.<\/p>\n<h3>3. Smart Queue<\/h3>\n<p>Backup is a resource-intensive task (CPU for compression, Disk I\/O for reading\/writing). If you schedule 10 containers to backup at the same time at midnight, your VPS will likely hang (High Load).<\/p>\n<p>Docker Guard integrates a smart queue (Queue) by default. Even if you click \u201cBackup All\u201d, it will process each container one by one (or 2-3 depending on Worker configuration). This container finishes before the next, ensuring the VPS always runs smoothly, never overloaded.<\/p>\n<h2>DIY Guide: Build Your Own Backup Tool<\/h2>\n<p>If you're interested and want to code a similar tool yourself, the key lies in communicating with the Docker Daemon.<\/p>\n<h3>Step 1: Mount Docker Socket<\/h3>\n<p>Docker Daemon listens for commands via a Unix Socket at <code data-no-translation=\"\">\/var\/run\/docker.sock<\/code>. To allow our Node.js app to control Docker, we need to mount this file into the app's container.<\/p>\n<pre data-no-translation=\"\"><code class=\"language-yaml\" data-no-translation=\"\"># docker-compose.yml\nservices:\n  my-backup-app:\n    image: node:18-alpine\n    volumes:\n      - \/var\/run\/docker.sock:\/var\/run\/docker.sock # Ch\u00eca kh\u00f3a l\u00e0 \u0111\u00e2y\n<\/code><\/pre>\n<h3>Step 2: Use Dockerode Library<\/h3>\n<p>Instead of calling shell commands <code data-no-translation=\"\">docker exec...<\/code> (very crude and hard to parse results), we use the library <code data-no-translation=\"\">dockerode<\/code>.<\/p>\n<pre data-no-translation=\"\"><code class=\"language-typescript\" data-no-translation=\"\">import Docker from 'dockerode';\n\n\/\/ K\u1ebft n\u1ed1i qua socket \u0111\u00e3 mount\nconst docker = new Docker({ socketPath: '\/var\/run\/docker.sock' });\n\nasync function backupContainer(containerId) {\n    const container = docker.getContainer(containerId);\n    \n    \/\/ 1. L\u1ea5y th\u00f4ng tin c\u1ea5u h\u00ecnh\n    const data = await container.inspect();\n    const env = data.Config.Env;\n    const image = data.Config.Image;\n    \n    console.log(`\u0110ang backup container: ${data.Name} - Image: ${image}`);\n    \n    \/\/ 2. X\u1eed l\u00fd backup volume (Logic n\u00e9n file)...\n    \/\/ ... code x\u1eed l\u00fd archiver (zip) ...\n}\n<\/code><\/pre>\n<h3>Step 3: Handle Stream (Data Stream)<\/h3>\n<p>For large data (several GB), absolutely do not read everything into RAM. Use Node.js Stream to \u201cpump\u201d data from Docker exec output directly into the compressed file.<\/p>\n<pre data-no-translation=\"\"><code class=\"language-typescript\" data-no-translation=\"\">\/\/ V\u00ed d\u1ee5 minh h\u1ecda pipeline\nimport { pipeline } from 'stream\/promises';\nimport fs from 'fs';\nimport zlib from 'zlib';\n\nawait pipeline(\n    dockerStream, \/\/ Stream t\u1eeb container\n    zlib.createGzip(), \/\/ N\u00e9n Gzip\n    fs.createWriteStream('.\/backup.tar.gz') \/\/ Ghi ra \u0111\u0129a\n);\n<\/code><\/pre>\n<h2>Summary<\/h2>\n<p>Building internal tools (Internal Tools) like <strong>Docker Guard<\/strong> not only makes operational work easier and safer, but also provides us with a great opportunity to learn more deeply about systems. Understanding Docker Socket, Streams, and System Architecture will help you become a more well-rounded software engineer.<\/p>\n<p>If you're also struggling with backups, try building a small tool for yourself. The feeling of watching progress bars run smoothly and green \u201cBackup Success\u201d notifications is really addictive!<\/p>\n<p><em>See you in the next technical sharing articles about DevOps and System Administration!<\/em><\/p>","protected":false},"excerpt":{"rendered":"<p>Introduction: The Nightmare Called \u201cManual Backup\u201d If you are a programmer, a System Admin, or simply a science enthusiast operating personal VPS machines, you must have experienced the \u201cchilling\u201d feeling when you accidentally [\u2026]<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-35751","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"acf":[],"rankmath_keywords":{"primary":"","secondary":[""]},"yoast_keywords":{"primary":"","secondary":[]},"yoast_focuskw":"","rankmath_focuskw":"","seo_keywords":{"primary":"","secondary":[""]},"_links":{"self":[{"href":"https:\/\/dps.media\/en\/wp-json\/wp\/v2\/posts\/35751","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dps.media\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dps.media\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dps.media\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dps.media\/en\/wp-json\/wp\/v2\/comments?post=35751"}],"version-history":[{"count":0,"href":"https:\/\/dps.media\/en\/wp-json\/wp\/v2\/posts\/35751\/revisions"}],"wp:attachment":[{"href":"https:\/\/dps.media\/en\/wp-json\/wp\/v2\/media?parent=35751"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dps.media\/en\/wp-json\/wp\/v2\/categories?post=35751"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dps.media\/en\/wp-json\/wp\/v2\/tags?post=35751"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}