{"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":"build-docker-custom-backup-system-deep-space-only-next-js-dockerode","status":"publish","type":"post","link":"https:\/\/dps.media\/en\/build-docker-custom-backup-system-deep-space-only-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>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>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>tar<\/code> (compression) of volume directories. But what if you lose the entire VPS? You have the data, but you forget what <code>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>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>.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'm 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 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>my-app_restored_20231025<\/code>.<\/li>\n<li>Map a random port (or one you choose) to avoid port conflicts with the main app (Live App).<\/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>\/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><code class=\"language-yaml\"># docker-compose.yml\nservices:\n  my-backup-app:\n    image: node:18-alpine\n    volumes:\n      - \/var\/run\/docker.sock:\/var\/run\/docker.sock # The key is here\n<\/code><\/pre>\n<h3>Step 2: Use Dockerode Library<\/h3>\n<p>Instead of calling shell commands <code>docker exec...<\/code> (very crude and hard to parse results), we use the library <code>dockerode<\/code>.<\/p>\n<pre><code class=\"language-typescript\">import Docker from 'dockerode';\n\n\/\/ Connect via mounted socket\nconst docker = new Docker({ socketPath: '\/var\/run\/docker.sock' });\n\nasync function backupContainer(containerId) {\n    const container = docker.getContainer(containerId);\n    \n    \/\/ 1. Get configuration info\n    const data = await container.inspect();\n    const env = data.Config.Env;\n    const image = data.Config.Image;\n    \n    console.log(`Backing up container: ${data.Name} - Image: ${image}`);\n    \n    \/\/ 2. Handle backup volume (File compression logic)...\n    \/\/ ... code handling 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><code class=\"language-typescript\">\/\/ Example pipeline illustration\nimport { pipeline } from 'stream\/promises';\nimport fs from 'fs';\nimport zlib from 'zlib';\n\nawait pipeline(\n    dockerStream, \/\/ Stream from container\n    zlib.createGzip(), \/\/ Gzip compression\n    fs.createWriteStream('.\/backup.tar.gz') \/\/ Write to disk\n);\n<\/code><\/pre>\n<h2>Summary<\/h2>\n<p>Building internal tools (Internal Tools) like <strong>Docker Guard<\/strong> not only makes operations work lighter and safer but is also a great opportunity for us to learn deeper about the system. Understanding Docker Socket, Streams, System Architecture will make you 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>\n<style>\r\n.lwrp.link-whisper-related-posts{\r\n            \r\n            margin-top: 40px;\nmargin-bottom: 30px;\r\n        }\r\n        .lwrp .lwrp-title{\r\n            \r\n            \r\n        }.lwrp .lwrp-description{\r\n            \r\n            \r\n\r\n        }\r\n        .lwrp .lwrp-list-container{\r\n        }\r\n        .lwrp .lwrp-list-multi-container{\r\n            display: flex;\r\n        }\r\n        .lwrp .lwrp-list-double{\r\n            width: 48%;\r\n        }\r\n        .lwrp .lwrp-list-triple{\r\n            width: 32%;\r\n        }\r\n        .lwrp .lwrp-list-row-container{\r\n            display: flex;\r\n            justify-content: space-between;\r\n        }\r\n        .lwrp .lwrp-list-row-container .lwrp-list-item{\r\n            width: calc(33% - 20px);\r\n        }\r\n        .lwrp .lwrp-list-item:not(.lwrp-no-posts-message-item){\r\n            \r\n            max-width: 150px;\r\n        }\r\n        .lwrp .lwrp-list-item img{\r\n            max-width: 100%;\r\n            height: auto;\r\n            object-fit: cover;\r\n            aspect-ratio: 1 \/ 1;\r\n        }\r\n        .lwrp .lwrp-list-item.lwrp-empty-list-item{\r\n            background: initial !important;\r\n        }\r\n        .lwrp .lwrp-list-item .lwrp-list-link .lwrp-list-link-title-text,\r\n        .lwrp .lwrp-list-item .lwrp-list-no-posts-message{\r\n            \r\n            \r\n            \r\n            \r\n        }@media screen and (max-width: 480px) {\r\n            .lwrp.link-whisper-related-posts{\r\n                \r\n                \r\n            }\r\n            .lwrp .lwrp-title{\r\n                \r\n                \r\n            }.lwrp .lwrp-description{\r\n                \r\n                \r\n            }\r\n            .lwrp .lwrp-list-multi-container{\r\n                flex-direction: column;\r\n            }\r\n            .lwrp .lwrp-list-multi-container ul.lwrp-list{\r\n                margin-top: 0px;\r\n                margin-bottom: 0px;\r\n                padding-top: 0px;\r\n                padding-bottom: 0px;\r\n            }\r\n            .lwrp .lwrp-list-double,\r\n            .lwrp .lwrp-list-triple{\r\n                width: 100%;\r\n            }\r\n            .lwrp .lwrp-list-row-container{\r\n                justify-content: initial;\r\n                flex-direction: column;\r\n            }\r\n            .lwrp .lwrp-list-row-container .lwrp-list-item{\r\n                width: 100%;\r\n            }\r\n            .lwrp .lwrp-list-item:not(.lwrp-no-posts-message-item){\r\n                \r\n                max-width: initial;\r\n            }\r\n            .lwrp .lwrp-list-item .lwrp-list-link .lwrp-list-link-title-text,\r\n            .lwrp .lwrp-list-item .lwrp-list-no-posts-message{\r\n                \r\n                \r\n                \r\n                \r\n            };\r\n        }<\/style>\r\n<div id=\"link-whisper-related-posts-widget\" class=\"link-whisper-related-posts lwrp\">\r\n            <div class=\"lwrp-title\">Related Posts<\/div>    \r\n        <div class=\"lwrp-list-container\">\r\n                                <div class=\"lwrp-list lwrp-list-row-container lwrp-list-double-row\">\r\n                <div class=\"lwrp-list-item\"><a href=\"https:\/\/dps.media\/en\/kich-thuoc-anh-bia-banner-sale-shopee-chuan-2021\/\" class=\"lwrp-list-link\"><span class=\"lwrp-list-link-title-text\">Cover Image Size, Shopee Sale Banner Standard 2021<\/span><\/a><\/div><div class=\"lwrp-list-item\"><a href=\"https:\/\/dps.media\/en\/what-is-a-needle-tree-viral-song-causing-a-social-media-sensation\/\" class=\"lwrp-list-link\"><span class=\"lwrp-list-link-title-text\">What is the Lucky Bamboo? The viral song causing a stir on social media<\/span><\/a><\/div><div class=\"lwrp-list-item\"><a href=\"https:\/\/dps.media\/en\/hollow-knight-silksong-officially-released-after-7-years-of-waiting\/\" class=\"lwrp-list-link\"><span class=\"lwrp-list-link-title-text\">Hollow Knight Silksong officially releases after 7 years of waiting<\/span><\/a><\/div>                <\/div>\r\n                            <div class=\"lwrp-list lwrp-list-row-container lwrp-list-double-row\">\r\n                <div class=\"lwrp-list-item\"><a href=\"https:\/\/dps.media\/en\/how-to-choose-the-right-reputable-press-booking-service\/\" class=\"lwrp-list-link\"><span class=\"lwrp-list-link-title-text\">Reputable press booking service: How to choose the right one?<\/span><\/a><\/div><div class=\"lwrp-list-item\"><a href=\"https:\/\/dps.media\/en\/how-to-livestream-selling-fruit-in-the-garden-attracting-urban-customers\/\" class=\"lwrp-list-link\"><span class=\"lwrp-list-link-title-text\">How to livestream selling fruit in the garden to attract urban customers<\/span><\/a><\/div><div class=\"lwrp-list-item\"><a href=\"https:\/\/dps.media\/en\/the-development-journey-of-dps-media-from-2017-to-now\/\" class=\"lwrp-list-link\"><span class=\"lwrp-list-link-title-text\">The development journey of DPS.MEDIA from 2017 to present<\/span><\/a><\/div>                <\/div>\r\n                <\/div>\r\n<\/div>","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}]}}