Skip to content
DevOps AI ToolKit
Newsletter

Kali Linux on Docker · Part 5 of 16

Docker Volumes and Persistent Kali Workspaces

Difficulty: Beginner ~14 min Part 5/16
Prerequisites: Kali Linux fundamentalsBasic Docker knowledge
Series progress5 / 16
Series curriculum (16 lessons)

A Kali container is meant to be thrown away. You start it, run a scan, read the output, and delete it — that disposability is exactly why running Kali in Docker is so pleasant. But the first time you save a report or capture a PCAP inside a container and then watch it vanish when the container exits, the obvious question follows: how do you keep the files when the container itself is disposable? The answer is Docker volumes. This lesson shows you how to keep your scripts, reports, packet captures, and config on the host while the container that produced them comes and goes.

The mental model: containers are cattle, files are not

There is a useful distinction here. The container is cattle — interchangeable, disposable, recreated on demand. Your work product — a scan report, a capture file, a helper script — is not disposable. You want it to outlive any single container.

By default a container has its own filesystem, layered on top of the image. When you write /root/report.txt inside a running Kali container, that file lives in the container’s writable layer. Delete the container and the writable layer goes with it. Nothing you wrote is left behind. That is the correct default: it is what makes the environment reproducible and clean. The trick is to carve out a specific directory that is not part of the container’s throwaway filesystem, and instead points at storage that persists. That is what a volume does.

Container filesystem vs. host filesystem

Think of two separate worlds:

  • The container filesystem — everything the container sees at /, assembled from the read-only image layers plus one thin writable layer on top. Ephemeral. Discarded when the container is removed.
  • The host filesystem — the real disk on your laptop or server, where your project directory lives. Persistent. Survives every container.

A volume is a doorway that connects a directory in the container filesystem to storage that persists independently of the container. Anything written through that doorway lands in persistent storage, not in the throwaway writable layer.

Bind mounts vs. named volumes

Docker gives you two main ways to persist data. They behave differently and you will use both.

  • Bind mount — you map a specific directory on your host to a path inside the container. You control the exact host location (for example, ./workspace in your project). Great for a working directory you want to open in your editor, commit to git, or inspect with normal host tools. This is what you want for a Kali workspace.
  • Named volume — Docker manages the storage for you in its own area (under /var/lib/docker/volumes/). You refer to it by name, not by host path. Great for data you want to persist but never need to browse directly from the host — think a tool’s cache or a database’s data directory.

Rule of thumb: if you want to see and edit the files with host tools, use a bind mount. If only the container needs the data and you just want it to survive restarts, use a named volume.

💡 Note — A bind mount ties the container to a specific host path, so it is slightly less portable between machines. A named volume is portable but opaque. For a hands-on Kali workspace you almost always want the bind mount, because the whole point is to read your reports and captures on the host.

Demonstration: a persistent workspace with a bind mount

Here is the core pattern. Assume you already built the kali-devops-toolbox image in an earlier lesson (see Build a Reusable DevOps Toolbox Image).

docker run --rm -it \
  -v "$PWD/workspace:/workspace" \
  kali-devops-toolbox

Every flag matters, so let us read it left to right:

  • docker run — create and start a new container.
  • --rm — automatically remove the container when it exits. The container is disposable; nothing left running, nothing to clean up.
  • -it — the combination of -i (keep STDIN open so you can type) and -t (allocate a TTY so you get a proper interactive shell). Together they give you an interactive terminal inside the container.
  • -v "$PWD/workspace:/workspace" — the volume flag, and the whole point of this lesson. It is a host:container mapping.
  • kali-devops-toolbox — the image to run.

Reading the -v host:container mapping

The -v value is two paths joined by a colon:

-v  <HOST PATH>  :  <CONTAINER PATH>
      $PWD/workspace   /workspace
  • Left of the colon — the host path. $PWD is your current directory, so $PWD/workspace is a real folder on your machine (Docker requires an absolute path here, which is why we expand $PWD rather than writing a bare workspace). If the directory does not exist yet, Docker creates it.
  • Right of the colon — the container path. /workspace is where that host folder appears inside the container.

So while you are inside the container, /workspace is not part of the throwaway filesystem — it is a live window onto ./workspace on your host. Write a file to /workspace/scan.txt in the container and it appears instantly at ./workspace/scan.txt on your host. Exit the container, let --rm delete it, and the file is still sitting on your host.

Here is the relationship as a picture:

Host

├── ./workspace
│       │
│       └───────────────┐
│                       │
└── Docker Engine       │
        │               │
        └── Kali Container

               └── /workspace

The ./workspace directory on the host and /workspace inside the container are the same bytes on disk, wired together by the Docker Engine through the bind mount.

🧪 Try It — Start the container with the command above. Inside it, run echo "hello from kali" > /workspace/hello.txt and then exit. Back on your host, run cat workspace/hello.txt. The file is there even though the container is gone.

What to persist: scripts, reports, PCAPs, and config

Once you have a /workspace bind mount, decide deliberately what belongs there. In practice, four kinds of files:

  • Scripts — the small helper scripts you write to automate a check (a DNS sweep, a batch of curl calls). Keep them in workspace/scripts/ so they survive and can be committed to git.
  • Reports — human-readable output you want to keep: nmap results, service inventories, findings. workspace/reports/.
  • PCAP files — packet captures from tcpdump. These are binary artifacts you will often re-open later in Wireshark on the host, which is exactly why a bind mount beats a named volume here. workspace/captures/. See Capture and Inspect Packets with tcpdump for the capture side.
  • Config — tool configuration, target lists, environment files you want stable across runs. workspace/config/.

A tidy layout inside workspace/ makes the container genuinely reusable:

workspace/
├── scripts/
├── reports/
├── captures/
└── config/

🔎 Troubleshooting Tip — If a file you saved “inside Kali” seems to have disappeared after you exited, check where you saved it. Only paths under the mounted /workspace persist. If you wrote to /root/report.txt or /tmp/, that lived in the container’s throwaway writable layer and is gone with the container. Before you exit, run ls -la /workspace to confirm your output actually landed on the mount, not somewhere ephemeral. This is classic expected state vs. observed state: you expected the file to persist, so verify it is on the mount rather than assuming.

The reusable kali-devops-lab/workspace/ project

Across this series we keep a single reusable lab project on the host, kali-devops-lab/, with a workspace/ directory that we bind-mount into every Kali container. Create it once:

mkdir -p kali-devops-lab/workspace/{scripts,reports,captures,config}
cd kali-devops-lab

Now the standard launch command from anywhere in that project is always the same:

docker run --rm -it \
  -v "$PWD/workspace:/workspace" \
  kali-devops-toolbox

Because the mount target is stable at /workspace, every lesson’s scripts and reports accumulate in one predictable place on the host. You can put kali-devops-lab/ under version control, share it with a teammate, or wipe a container and pick up exactly where you left off. The container is disposable; kali-devops-lab/workspace/ is your durable home base.

🛠️ DevOps Perspective — This is the same discipline you apply to any stateless service: keep the runtime disposable, keep state on a named, backed-up location. A Kali container is just another stateless workload, and workspace/ is its externalized state.

Named volumes, briefly

When you don’t need to browse the files from the host, a named volume is cleaner:

docker volume create kali-cache
docker run --rm -it \
  -v kali-cache:/root/.cache \
  kali-devops-toolbox

Here the left side of the -v is a name (kali-cache), not a path, so Docker stores the data in its own managed area and reattaches it every time you use that name. Good for caches; not what you want for reports you plan to read on the host.

Common Problems

Real symptoms you will hit, and how to work through them.

Permission denied writing a mounted volume

Symptom. Inside the container you try to write to /workspace and get Permission denied, or files you create on the host as your user cannot be modified inside the container (or vice versa).

Why it happens. A bind mount does not translate ownership. The files carry the same numeric uid/gid on both sides. The official Kali image runs as root (uid 0), while your host user is typically uid 1000. Root-in-container can usually write anywhere, but the files it creates end up owned by uid 0 on your host — so back on the host your normal user may be unable to edit or delete them. The reverse also bites: if the container process runs as a non-root user, it may not be able to write into a host directory owned by your uid 1000.

Diagnose it.

# On the host — who owns the workspace and its files?
ls -la workspace

# Inside the container — what uid/gid am I, and who owns the mount?
id
ls -la /workspace

Compare the numbers. A mismatch between the container process uid and the owner of the host directory is the root cause almost every time.

Fix it. A few options, least surprising first:

  • Run the container as your own host uid/gid so new files are owned by you on both sides:

    docker run --rm -it \
      -u "$(id -u):$(id -g)" \
      -v "$PWD/workspace:/workspace" \
      kali-devops-toolbox

    -u <uid>:<gid> overrides the container’s default user. Note that some Kali tools expect root, so this works best for editing scripts and reports, less so for privileged capture tools.

  • Or fix ownership of already-created files back on the host:

    sudo chown -R "$(id -u):$(id -g)" workspace
  • Or, if a specific tool truly needs root, let it run as root and just re-own the outputs afterward with the chown above.

🔐 Security Note — Running as root inside a container is not “safe because it’s a container.” Containers share the host kernel, and a bind-mounted host directory is a direct bridge from container-root to your host filesystem. A process running as root in the container writes files as root on your host through the mount, and only the mounted path is exposed — but that is still real host access. Mount the narrowest directory you need (./workspace, never your home directory or /), and prefer -u to drop to your own uid when the tool allows it. Least privilege applies inside containers too.

The mount looks empty or the wrong thing appears

Symptom. /workspace inside the container is empty, or shows unexpected contents.

Diagnose. Confirm the host path resolved to what you meant: echo "$PWD/workspace". Remember Docker needs an absolute path on the left of the -v; a bare relative name can be interpreted as a named volume instead of a bind mount, which silently creates an empty managed volume. Run docker inspect <container> and read the Mounts section to see whether Docker recorded a bind or a volume.

Changes on the host don’t appear in the container

Symptom. You edit a file on the host but the container still sees the old version.

Diagnose. A bind mount is live — edits should appear immediately. If they do not, you are usually looking at two different directories: check that the container was started from the directory you think, so $PWD expanded correctly. On some Docker Desktop setups, verify the host path is inside a directory shared with the Docker VM.

Try It Yourself

Only work against your own local containers and lab directories here.

  1. Create the reusable lab: mkdir -p kali-devops-lab/workspace/{scripts,reports,captures,config} && cd kali-devops-lab.
  2. Launch Kali with the workspace mounted:
    docker run --rm -it -v "$PWD/workspace:/workspace" kali-devops-toolbox
  3. Inside the container, write a report: echo "lab inventory $(date)" > /workspace/reports/day1.txt.
  4. Confirm it is on the mount: ls -la /workspace/reports.
  5. Exit the container (--rm deletes it), then on the host run cat workspace/reports/day1.txt. The container is gone; your report remains.
  6. Check ownership on the host with ls -la workspace/reports. If the file is owned by root, re-run step 2 adding -u "$(id -u):$(id -g)" and compare.

🏭 Why This Matters in Production — In CI/CD and on shared build agents, this exact pattern is how ephemeral security-scan containers hand their results back to the pipeline: the container runs a scan, writes a report to a mounted workspace, then is destroyed, and the pipeline archives the report from the host. The container’s disposability is a feature — reproducible, no drift, nothing left running — while the evidence (reports, captures, findings) is deliberately persisted on durable storage. Get the mount and the uid/gid ownership right in your lab and you have already learned the production shape: disposable runtime, persistent artifacts, least-privilege access to the host.

What You Learned

  • Containers are disposable, but files written through a volume persist independently of any container.
  • The container filesystem is ephemeral (image layers plus a throwaway writable layer); the host filesystem is durable — a bind mount wires a specific host directory to a path inside the container.
  • -v "$PWD/workspace:/workspace" is a host:container mapping: left of the colon is the real host directory, right is where it appears inside Kali.
  • Use a bind mount for a workspace you read and edit on the host (scripts, reports, PCAPs, config); use a named volume for container-only data like caches.
  • Keep a reusable kali-devops-lab/workspace/ project so every disposable container writes to one predictable, durable place.
  • Permission denied on a mount is almost always a uid/gid mismatch between host and container — diagnose with id and ls -la, and fix with -u "$(id -u):$(id -g)" or chown.

Next, wire your persistent workspace into a purpose-built image — see Build a Custom Kali Docker Image — or start capturing traffic into it with tcpdump packet capture. Return to the Kali Linux hub for the full path.

Affiliate Disclosure: Some links on this page are affiliate links. If you purchase through one of these links, DevOps AI Toolkit may earn a commission at no additional cost to you. See our affiliate disclosure.

← Back to Kali Linux on Docker Back to Kali Linux

Related on DevOps AI Toolkit