Skip to main content
Understanding data persistence in the TurtleBot3 dev container is crucial for knowing which files are preserved and which are ephemeral. This page explains the persistence model and how to ensure your work is never lost.

Persistence model

The dev container uses a bind mount strategy to persist your work while keeping the container environment reproducible.

What is a bind mount?

A bind mount creates a direct mapping between a directory on your host machine and a directory inside the container. Files written to either location appear in both.
Host Machine                    Container
┌─────────────────────┐        ┌──────────────────────────┐
│ turtlebot3_jazzy_   │        │ /workspace/              │
│   devcontainer/     │◄──────►│   turtlebot3_ws/         │
│                     │  Bind  │                          │
│ ├── src/            │  Mount │ ├── src/                 │
│ ├── .devcontainer/  │        │ ├── .devcontainer/       │
│ ├── .gitignore      │        │ ├── .gitignore           │
│ └── README.md       │        │ └── README.md            │
└─────────────────────┘        └──────────────────────────┘
Configuration in devcontainer.json:
"workspaceFolder": "/workspace/turtlebot3_ws",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace/turtlebot3_ws,type=bind"
  • source: Your project folder on the host
  • target: Mount point inside the container
  • type=bind: Direct filesystem mapping
Bind mounts ensure changes are immediately visible in both locations. No sync delay, no data copying.

Persisted data

Files in the workspace folder are automatically persisted to your host machine.

Source code

All ROS2 packages in the src/ directory are persisted:
src/
├── DynamixelSDK/              # Motor control SDK
├── turtlebot3/                # Core TurtleBot3 packages
├── turtlebot3_msgs/           # Message definitions
├── turtlebot3_simulations/    # Gazebo simulation
└── your_custom_package/       # Your custom packages
Cloned during: post-create.sh (first container creation) Persisted: Yes, via bind mount Safe to modify: Yes Version controlled: Yes (each subdirectory is a git repository)

Configuration files

Dev container configuration files are persisted:
.devcontainer/
├── Dockerfile           # Container image definition
├── devcontainer.json    # VS Code configuration
├── post-create.sh       # Initial setup script
└── post-start.sh        # Startup build script
Persisted: Yes Safe to modify: Yes (requires container rebuild to take effect) Version controlled: Yes

Custom launch files and maps

Any files you create in the workspace are persisted:
  • Custom launch files in package directories
  • Saved maps (typically in ~/maps/ - see note below)
  • Configuration files
  • Scripts and utilities
Files saved to the home directory (~/) inside the container are not persisted unless they’re in the workspace. Save maps to /workspace/turtlebot3_ws/maps/ instead of ~/maps/ for persistence.

Project documentation

Readme files and documentation in the workspace:
README.md
LICENSE
.gitignore
Persisted: Yes Safe to modify: Yes Version controlled: Yes

Ephemeral data

These files exist only while the container is running and are regenerated as needed.

Build artifacts

Colcon build outputs are not persisted by default:
build/      # CMake build files and object files
install/    # Compiled binaries and libraries
log/        # Build and runtime logs
Why not persisted?
  • Can be regenerated from source with colcon build
  • Often contain platform-specific binaries
  • Large file sizes (hundreds of MB)
  • May cause issues if host and container architectures differ
Regenerated by: post-start.sh on every container start
The .gitignore file explicitly excludes build/, install/, and log/ directories from version control.

Container filesystem

Everything outside the workspace mount is ephemeral:
  • Installed apt packages (defined in Dockerfile)
  • System configuration in /etc/
  • User home directory ~/ (except workspace)
  • Temporary files in /tmp/
Persistence strategy: Define in Dockerfile, not manually installed

Container state

Running processes and runtime state:
  • Running ROS2 nodes
  • Gazebo simulation state
  • Terminal sessions
  • Environment variables set in current session (not in .bashrc)
Recreated: On every container start or terminal creation

Special directories

Home directory (~/)

The container home directory is not persisted by default. Location inside container: /home/<username>/ Persisted: No Contains:
  • .bashrc (configured by post-create.sh)
  • .bash_history
  • ROS2 logs
  • Gazebo cache (.gz/)
  • VNC configuration (.vnc/)
Workaround for persistence: Create a symlink from workspace to home:
# Inside container
ln -s /workspace/turtlebot3_ws/my_persistent_data ~/my_data
Now ~/my_data persists because it’s actually in the workspace.

Maps directory

By default, post-create.sh creates ~/maps/, which is not persisted. Problem: Maps saved during SLAM will be lost when container is deleted. Solution: Use a persisted location:
# Create maps directory in workspace
mkdir -p /workspace/turtlebot3_ws/maps

# Save maps there
ros2 run nav2_map_server map_saver_cli -f /workspace/turtlebot3_ws/maps/my_map
Or create a symlink:
rmdir ~/maps
ln -s /workspace/turtlebot3_ws/maps ~/maps
Add map symlink creation to post-create.sh to make it automatic for all developers.

ROS2 logs

ROS2 runtime logs are stored in ~/.ros/log/ and are not persisted. Location: /home/<username>/.ros/log/ Persisted: No Persistence workaround:
# Redirect ROS log directory to workspace
export ROS_LOG_DIR=/workspace/turtlebot3_ws/ros_logs
Add to .bashrc in post-create.sh for permanence.

Container lifecycle

Container creation

First time opening the dev container:
  1. Image build: Dockerfile instructions execute
    • Installs all packages
    • Takes 10-15 minutes
    • Image is cached for reuse
  2. Container creation: Docker creates container from image
    • Mounts workspace folder
    • Forwards ports
    • Applies runtime configuration
  3. Post-create hook: post-create.sh runs
    • Clones TurtleBot3 repositories to src/
    • Configures .bashrc
    • Installs dependencies
    • Source code is now persisted
  4. Post-start hook: post-start.sh runs
    • Builds workspace with colcon build
    • Build artifacts are ephemeral

Container restart

When you reopen the project:
  1. Container start: Existing container resumes
    • Workspace mount reconnects
    • Persisted source code is available
    • Build artifacts may or may not exist
  2. Post-start hook: post-start.sh runs
    • Checks if workspace is built
    • Rebuilds if necessary
    • Ensures workspace is always ready

Container rebuild

When you explicitly rebuild (F1 → “Rebuild Container”):
  1. Container deletion: Old container is removed
    • Ephemeral data lost
    • Persisted workspace untouched
  2. Image rebuild: Dockerfile instructions re-execute (if needed)
  3. New container creation: Fresh container created
  4. Post-create hook: Runs again
    • Skips cloning if repositories already exist
    • Re-configures .bashrc
  5. Post-start hook: Runs
    • Rebuilds workspace from persisted source
The post-create.sh script checks if TurtleBot3 repositories exist before cloning, so rebuilding doesn’t duplicate packages.

Best practices

Keep source in workspace

Always create or clone packages inside the workspace:
# Good - persisted
cd /workspace/turtlebot3_ws/src
ros2 pkg create --build-type ament_cmake my_package

# Bad - lost on container deletion
cd ~/my_packages
ros2 pkg create --build-type ament_cmake my_package

Use version control

The workspace is designed for git:
cd /workspace/turtlebot3_ws
git add src/my_custom_package
git commit -m "Add custom package"
git push
Your custom packages are version controlled alongside the dev container configuration.

Save important files to workspace

Move critical files to the workspace:
# Maps
mkdir -p /workspace/turtlebot3_ws/maps
ros2 run nav2_map_server map_saver_cli -f /workspace/turtlebot3_ws/maps/my_map

# Configuration files
cp my_config.yaml /workspace/turtlebot3_ws/config/

# Launch files
cp my_launch.py /workspace/turtlebot3_ws/src/my_package/launch/

Document persistent paths

If your package expects files in specific locations, document where to save them:
# my_package/README.md

## Saving Maps

Maps must be saved to the workspace for persistence:

```bash
ros2 run nav2_map_server map_saver_cli -f /workspace/turtlebot3_ws/maps/my_map
Do not save to ~/maps/ as this is not persisted.

### Rebuild vs. restart

**Use restart** (just close and reopen VS Code):
- Source code changes
- Package development
- Day-to-day work

**Use rebuild** (F1 → "Rebuild Container"):
- Modified Dockerfile
- Changed devcontainer.json configuration
- Container issues or corruption
- Updated post-create.sh or post-start.sh

## Data loss scenarios

### What causes data loss?

1. **Container deletion** without bind mount
   - Lost: Everything outside workspace
   - Preserved: All workspace files

2. **Host filesystem deletion**
   - Lost: Everything (backup your work!)
   - No container can save you from deleting the host folder

3. **Docker system prune**
   - Lost: Containers, images, volumes
   - Preserved: Workspace files (they're on the host)

### What doesn't cause data loss?

1. **Container restart**: Workspace persists
2. **Container rebuild**: Workspace persists
3. **Docker Desktop restart**: Workspace persists
4. **Host reboot**: Workspace persists
5. **VS Code update**: Workspace persists

<Warning>
The only way to lose workspace files is to delete them from the host filesystem. Always commit important changes to git.
</Warning>

## Recovery procedures

### If build artifacts are corrupted

```bash
cd /workspace/turtlebot3_ws
rm -rf build install log
colcon build --symlink-install
Source code is safe; only regenerates build outputs.

If TurtleBot3 repositories are missing

Run post-create script again:
cd /workspace/turtlebot3_ws
bash .devcontainer/post-create.sh
Script will detect missing repositories and clone them.

If container is unrecoverable

Rebuild from scratch:
  1. Close VS Code
  2. Delete container: docker rm -f <container-name>
  3. Delete image: docker rmi <image-name>
  4. Open project in VS Code
  5. Reopen in container
  6. Wait for rebuild and post-create
Your source code in src/ will still be there because it’s on the host.

Advanced persistence: Docker volumes

For advanced users, Docker volumes can persist additional directories.

Adding a volume for home directory

Edit devcontainer.json:
"mounts": [
  "source=turtlebot3-home,target=/home/<username>,type=volume"
]
This persists the home directory across container rebuilds (but not across container deletions).

Named volumes vs. bind mounts

Bind mount (current approach):
  • Direct host folder mapping
  • Files accessible from both host and container
  • Easy to backup, version control, and inspect
  • Used for: Workspace source code
Named volume:
  • Docker-managed storage
  • Not directly accessible from host
  • Better performance on non-Linux systems
  • Potential use for: Home directory, cache directories
The default configuration uses bind mounts for simplicity and accessibility. Volumes are optional optimizations.

Build docs developers (and LLMs) love