Run a full Linux development environment on your NAS β safely inside a container β without modifying the NAS operating system.
NAS DevBox turns your NAS into a remote Linux workstation while keeping Docker and storage on the host.
This project started from the architecture described in this article:
- Full Ubuntu environment accessible via SSH
- No changes to the NAS OS
- Projects stored directly on NAS storage
- Manage host Docker from inside the dev container
- Isolated, disposable development environment
Ideal for homelabs, self-hosted setups, and lightweight remote development.
Your PC ββ SSH βββΊ Dev Container (Ubuntu)
β
βΌ
Docker Engine (NAS)
β
βΌ
Project Containers
How it works
- The NAS runs Docker normally
- DevBox runs an Ubuntu container with development tools
- Your projects live on the NAS and are mounted into
/projects /home/<user>/projectsis a symlink to/projects/workspacecan be enabled as a compatibility symlink- The container controls host Docker via the Docker socket
No changes to the NAS firmware or OS are required.
- NAS with Docker installed
- SSH access to the NAS
- A directory for projects on the NAS (e.g.
/volume1/projects)
mkdir -p /volume1/projectsmkdir -p /volume1/projects/devbox
cd /volume1/projects/devbox
git clone https://github.com/loglux/NAS-DevBox.git .Use .env as the primary configuration source so recreate/start commands stay short and consistent.
cp .env.example .env
# edit .env and set at least:
# DEVBOX_USER, DEVBOX_PASS, DEVBOX_SSH_PORT, DEVBOX_PROJECTS_DIR
# optionally: DEVBOX_HOME_DIR, DEVBOX_START_DIR, DEVBOX_PASSWORDLESS_SUDOMinimal example:
DEVBOX_USER=loglux
DEVBOX_PASS=your-strong-password
DEVBOX_SSH_PORT=2202
DEVBOX_PROJECTS_DIR=/volume1/projectsdevbox.sh loads config in this order:
- script defaults
./.env./.env.local(optional override)--env-file <path>(if provided)- CLI flags (highest priority)
| Variable | CLI flag | Description | Default |
|---|---|---|---|
| DEVBOX_USER | --user |
Container username | dev |
| DEVBOX_PASS | --pass |
User password | changeme |
| DEVBOX_SSH_PORT | --ssh-port |
SSH port | 2202 |
| DOCKER_GID | --docker-gid |
Docker group ID | 1000 |
| DEVBOX_UID | --uid |
Container user UID | host id -u (auto if empty) |
| DEVBOX_GID | --gid |
Container user GID | host id -g (auto if empty) |
| DEVBOX_PROJECTS_DIR | --projects-dir |
Projects path on NAS | /volume1/projects |
| DEVBOX_WORKSPACE_LINK | --workspace-link |
Create /workspace compatibility symlink |
on |
| DEVBOX_START_DIR | --start-dir |
Auto-cd target on interactive login | /workspace |
| DEVBOX_PASSWORDLESS_SUDO | --passwordless-sudo |
Passwordless sudo mode | on |
| DEVBOX_HOME_DIR | --home-dir |
Persistent home dir on host | auto-resolved by devbox.sh |
| DEVBOX_CONTAINER_NAME | --container |
Container name | devbox |
| POST_INSTALL_TARGET | --post-install |
Post-install target(s), comma-separated | empty |
Flags override environment variables.
Note: auto-resolution for DEVBOX_HOME_DIR applies only when DEVBOX_HOME_DIR is empty/unset.
Note: POST_INSTALL_TARGET supports one or many targets, for example ai or dev,ai.
- projects mount:
${DEVBOX_PROJECTS_DIR}->/projects - settings/home mount:
${DEVBOX_HOME_DIR}->/home/<user> - convenience symlink:
/home/<user>/projects->/projects - optional compatibility symlink:
/workspace->/home/<user>/projects(only whenDEVBOX_WORKSPACE_LINK=on)
Minimal start:
./devbox.shFull profile (main + playwright + AI tools):
./devbox.sh --playwright --post-install aiOne-time CLI override example (without editing .env):
./devbox.sh --user loglux --pass 'your-strong-password' --ssh-port 2202What devbox.sh does:
- passes your parameters to Docker build/runtime
- builds or rebuilds the DevBox image
- starts the container in background mode
- mounts your NAS projects directory to
/projects - connects container to host Docker via
/var/run/docker.sock - if
--recreateis used, removes old container before fresh start
ssh <user>@NAS_IP -p <ssh_port>Your projects are available inside the container at:
/projects
Convenience paths:
/home/<user>/projects
/workspace (optional, only when DEVBOX_WORKSPACE_LINK=on)
- Username: value of
DEVBOX_USER(or--user) - Password: value of
DEVBOX_PASS(or--pass) - If you do not set them, defaults are
dev/changeme
Recommended:
- set
DEVBOX_USERandDEVBOX_PASSin.envbefore first start - use
--user/--passonly for temporary override
From the NAS host:
docker exec -it devbox passwd <user>Inside the container:
docker psYou should see containers running on the NAS host.
Expected DevBox container names by default:
devboxdevbox-playwright(only when started with--playwright)
You can customize names via:
DEVBOX_CONTAINER_NAMEDEVBOX_PLAYWRIGHT_CONTAINER_NAME
Quick host-side check:
docker ps --format 'table {{.Names}}\t{{.Status}}' | grep -E 'devbox|devbox-playwright'Passwordless sudo is enabled by default (DEVBOX_PASSWORDLESS_SUDO=on).
To disable it, set DEVBOX_PASSWORDLESS_SUDO=off or use --passwordless-sudo off.
Why this is useful:
- install system packages without interactive password prompts (
sudo apt ...) - run setup/bootstrap scripts non-interactively
- simplify automation from CLI tools inside the container
Recreate removes the existing container and builds a fresh one.
Important:
- Recreate replaces containers and applies current config values
- Keep your preferred values in
.envso you do not need to pass all flags each time - The password resets to
changemeonly if neither.envnor CLI setsDEVBOX_PASS/--pass - Existing project data is safe (stored on NAS)
./devbox.sh --recreateRecommended recreate flow:
# main + playwright + AI tooling
./devbox.sh --recreate --playwright --post-install ai
# custom env file
./devbox.sh --recreate --env-file /path/to/my.env- The container has access to the host Docker socket
- Anyone with container access can control host containers
- Use strong passwords or SSH keys
- Consider restricting network exposure
This setup is useful if you want to:
- Develop directly on NAS storage
- Keep your NAS OS untouched
- Run disposable dev environments
- Use NAS as a remote build machine
- Manage Docker workloads remotely
To avoid losing shell/Codex/SSH settings after recreate, mount one persistent home directory:
DEVBOX_HOME_DIR->/home/<user>
Default resolution in devbox.sh:
- explicit
DEVBOX_HOME_DIR/--home-dir /volume1/home/<user>if it exists/home/<user>if it exists- fallback:
/volume1/projects/.devbox-home/<user>
DevBox creates the container user from these values:
DEVBOX_USERDEVBOX_UIDDEVBOX_GID
By default, devbox.sh uses your current host id -u and id -g.
That keeps file ownership in your mounted projects path aligned with your host user.
You can still override them explicitly in .env or via CLI flags.
Example:
./devbox.sh --recreate \
--user devuser \
--uid 1000 \
--gid 1000 \
--ssh-port 2202 \
--projects-dir /volume1/projectsProjects are always mounted to:
/projects
User convenience symlink:
/home/<user>/projects->/projects
Optional symlink:
/workspace->/home/<user>/projects->/projects
Control it with:
DEVBOX_WORKSPACE_LINK=on|off- CLI:
--workspace-link on|off
When you enable --playwright, DevBox starts a second container (devbox-playwright) in addition to the main devbox.
Why a second container is used:
- Browser automation often needs relaxed sandbox settings that are not needed for normal CLI development.
- These permissions are applied only to
devbox-playwright, while the maindevboxstays stricter. - This keeps day-to-day development and browser automation separated.
Playwright container runtime settings:
security_opt: seccomp=unconfinedcap_add: SYS_ADMINipc: host
Start both containers (main + playwright):
./devbox.sh --playwright --recreate \
--user devuser \
--ssh-port 2202 \
--playwright-ssh-port 2203SSH endpoints:
- Main devbox:
ssh <user>@<NAS_IP> -p 2202 - Playwright devbox:
ssh <user>@<NAS_IP> -p 2203
Typical usage:
- Use
devbox(2202) for normal coding, tooling, and shell work. - Use
devbox-playwright(2203) only for browser automation tasks.
For connecting external tools (Playwright scripts, MCP clients, Claude Code) to the container's Chrome instance via CDP, see docs/playwright-cdp-setup.md.
To keep the base image neutral, extra tools are installed via optional post-install scripts. This option is independent from the Playwright profile.
Built-in targets:
example->/projects/devbox/scripts/post-install-example.shdev->/projects/devbox/scripts/post-install-dev.shai->/projects/devbox/scripts/post-install-ai.shmigrate->/projects/devbox/scripts/post-install-migrate.sh(one-time optional recovery)
Run with:
./devbox.sh --post-install exampleMultiple targets in one run:
./devbox.sh --post-install dev --post-install ai
# or
./devbox.sh --post-install dev,aiPlaywright can be combined with any post-install target (optional):
./devbox.sh --playwright --post-install devAI tooling profile:
./devbox.sh --post-install aiThis installs Node.js/npm and AI CLI tools (codex, claude).
Migration profile (explicit opt-in, no hardcoded source paths):
# in /projects/devbox/.env or .env.local
MIGRATE_CODEX_FROM=/projects/<your-old-path>/.codex
MIGRATE_BASH_HISTORY_FROM=/projects/<your-old-path>/.bash_history
./devbox.sh --post-install migrateCustom script path (absolute or relative to mounted projects directory):
./devbox.sh --post-install /projects/my-scripts/post-install.sh
# or
./devbox.sh --post-install my-scripts/post-install.shThis allows every user to keep their own tool stack without forcing it into the default image.
MIT