📖 Chapter 08 — Remote Development
The dev-server setup. The same code, the same data, the same environment — from any device.
Learning Objectives
- Define the remote development model: thin client, fat server
- Set up VS Code Server (code-server) on the NAS
- Set up the SSH dev workflow (sshfs, port forwarding, VS Code Remote)
- Use remote Jupyter for GPU-bound notebooks
- Know when to use local vs remote development
Introduction
From Volume 5, Chapter 2: JupyterLab on the NAS is one form of remote development. This chapter is the broader pattern: the same development experience from any device, with the code, the data, and the compute on the NAS. The laptop is a thin client; the NAS is the dev server. The same files, the same Python environment, the same GPU (if you have one), the same network access to the data — all from a browser or a small SSH session.
The conversation was clear: this is the right model for a home data scientist. The local machine is for emails and the occasional flight. The NAS is for real work.
The thin client, fat server model
The traditional model: the laptop is the dev machine. The code, the data, the Python environment, the GPU are all on the laptop. You're tied to the laptop; switching machines means syncing files; the GPU is the laptop's GPU (small, expensive, hot).
The remote development model: the NAS is the dev machine. The code lives on the NAS. The data lives on the NAS. The Python environment lives on the NAS. The GPU (if you have one) is on the NAS. The laptop is a thin client: a browser, an SSH client, maybe a small terminal. You're not tied to the laptop; you can use any device with a browser.
The trade-offs:
- Pros: same environment everywhere; data lives next to the code; GPU (when you have one) is shared; laptop can be cheap; battery life doesn't matter for heavy work.
- Cons: requires network connectivity; the NAS must be on (no working on a plane); latency-sensitive tasks (interactive GUIs) can feel laggy; debugging is sometimes harder.
For data science, the pros dominate. The data is on the NAS; the model is on the NAS; the environment is on the NAS. Working on a laptop means syncing the data, syncing the model, syncing the environment — and the data is too big to sync.
Option 1: VS Code Server (code-server)
code-server is the open-source version of GitHub's VS Code that runs in a browser. The setup:
- Install code-server in a container (or via the TrueNAS Apps catalog; it's in the community list).
- Configure the port (default 8080), the password (or disable it if you're behind Tailscale), the working directory.
- Access via http://nas-ip:8080 in a browser. The full VS Code UI is in the browser, including the terminal, the extensions, the file explorer.
For most home data scientists, code-server is the right answer. The full VS Code experience, in a browser, with the code on the NAS, with the Python environment on the NAS, with the data on the NAS. From any device.
Option 2: VS Code Remote (SSH)
VS Code on the laptop can connect to a remote server over SSH. The setup:
- On the laptop, install the "Remote - SSH" extension in VS Code.
- Add an SSH config entry for the NAS (or use Tailscale's hostname).
- Connect to the NAS. VS Code opens a remote window; the UI is on the laptop, the code is on the NAS.
- Install the Python extension on the remote server. The Python interpreter is the NAS's Python; the kernel runs on the NAS.
The advantage over code-server: the laptop has the full VS Code experience with all the local features (better terminal, better font rendering, native file dialogs). The disadvantage: requires VS Code on the laptop; not browser-based.
Option 3: SSH dev workflow (no IDE)
The old-school approach: SSH into the NAS, use vim or nano or emacs, run the Python from the terminal, forward the Jupyter port if needed. The setup:
- SSH into the NAS:
ssh [email protected](or via Tailscale). - Use the terminal for everything. The code is in
~/projects/<name>/on the NAS. - For Jupyter: start the notebook server on the NAS, forward the port (
ssh -L 8888:localhost:8888 tk@nas), open http://localhost:8888 in the laptop's browser.
For quick edits, debugging, running scripts: the SSH workflow is enough. For long sessions, heavy development: code-server or VS Code Remote is more comfortable.
Remote Jupyter for GPU workloads
For AI/ML training that needs a GPU, the pattern:
- Add a GPU to the NAS (or use a burst GPU in the cloud — see Volume 4, Chapter 10).
- Install the NVIDIA drivers and the CUDA toolkit on the NAS.
- Install the GPU version of PyTorch or TensorFlow in the Python environment.
- Start Jupyter on the NAS; the GPU is available to the notebooks automatically.
- Connect from the laptop: the laptop is the UI, the NAS does the compute.
The notebook is on the NAS, the data is on the NAS, the GPU is on the NAS, the kernel is on the NAS. The laptop is just rendering the output. For multi-hour training runs, the laptop can be closed; the training continues on the NAS.
The SSH config for daily use
For TK's setup, the SSH config on the laptop (~/.ssh/config):
Host nas
HostName 100.x.y.z # Tailscale IP of the NAS
User tk
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 60
Host nas-jup
HostName 100.x.y.z
User tk
LocalForward 8888 localhost:8888
ServerAliveInterval 60
Now ssh nas connects to the NAS, and ssh nas-jup connects and forwards the Jupyter port. The Tailscale IP means the connection is encrypted and authenticated without exposing SSH to the public internet.
The "file sync" question
For projects that have small code but large data, the right answer is: don't sync the data. The data lives on the NAS; the code lives on the NAS; you work on the NAS. The laptop is a thin client.
For projects where the code needs to be on the laptop (rare, but it happens — mobile development, for example), use Syncthing (Volume 3, Chapter 3) to sync the code directory. The data is still on the NAS; the code is on both.
The "what about git" question
Git is the standard for code versioning. The discipline:
- Every project is a Git repository on the NAS.
- Commits happen on the NAS (the working tree is the NAS).
- Push to a remote (GitHub, GitLab, self-hosted Gitea) for backup and collaboration.
From any device with remote access to the NAS, the Git workflow is the same: pull, edit, commit, push. The laptop is irrelevant; the code is on the NAS and the remote.
The "what about the IDE" question
For data science, the most-used IDEs are VS Code, PyCharm, and JupyterLab. The remote development story for each:
- VS Code: Remote - SSH or code-server. Both work well.
- PyCharm Professional: has built-in remote interpreters. Configure the NAS's Python as the interpreter; the code is on the NAS, the UI is on the laptop.
- JupyterLab: runs on the NAS, accessed from the laptop's browser via port forwarding. This is the simplest setup for notebook-heavy work.
For most data scientists, the laptop has VS Code (or PyCharm) for code editing and the browser for JupyterLab. The two are connected to the NAS; the NAS has the code, the data, the environment.
The "what if I'm offline" question
Working offline (on a plane, in a coffee shop with bad wifi) is a real constraint. The options:
- Accept the constraint. The remote development model assumes connectivity. Without it, the work doesn't happen. (For a data scientist, this is usually fine — the analysis is the work, and the data is on the NAS.)
- Pull a copy of the code. Use Git to pull the latest code to the laptop; work on the laptop; push the changes when reconnected. This works for code-only work; it doesn't work for data work (the data is too big).
- Pull a copy of the data. For small datasets, this is fine. For large datasets, the sync is slow and the laptop's storage is the constraint.
For TK's workflow, the right answer is: accept the constraint. The data is on the NAS; the work is on the NAS; the offline constraint is a feature, not a bug. The work that can be done offline (writing, planning, reading) is what gets done offline. The work that needs the data is what gets done at home or via Tailscale.
Security: the remote access question
From Volume 1, Chapter 8: the NAS is not exposed to the public internet. Remote access is via Tailscale. For remote development, the same rule applies:
- code-server is reachable only on the Tailscale network (or behind Cloudflare Access if you need a public URL with login).
- SSH is on the Tailscale network (or on a non-standard port if you must expose it, with fail2ban and key-based auth).
- Jupyter is on the Tailscale network (or on a random port with a token in the URL).
The discipline: never expose dev tools to the public internet. Use Tailscale (or Cloudflare Access) for remote access. The 30 minutes to set up Tailscale is the price of not getting your dev environment compromised.
The "what if I add a second machine" question
When the lab grows (a second NAS, a dedicated GPU server, a build server), the remote development model scales naturally:
- Each machine runs code-server (or SSH) and Jupyter.
- The laptop connects to whichever machine has the right resources for the current task.
- Git is the coordination layer; the data is shared via the network filesystem (NFS, SMB) or via S3-compatible storage.
For TK's build, the second machine might be a dedicated GPU server for ML training. The workflow: edit code in Jupyter on the NAS; submit the training job to the GPU server; retrieve the trained model back to the NAS. The laptop is the same thin client throughout.
Engineering Note
The thin client, fat server model is the home data scientist's superpower. The data is on the NAS. The code is on the NAS. The environment is on the NAS. The GPU (when you have one) is on the NAS. The laptop is a browser. The setup takes an afternoon; the benefit is years of "any device, same code, same data, same environment." The discipline: resist the temptation to work on the laptop. Pull the data, sync the environment, do the work on the NAS. The laptop is for the plane; the NAS is for the work.
Summary
Remote development: thin client (laptop), fat server (NAS). The data, code, and environment live on the NAS. Three options: code-server (VS Code in the browser), VS Code Remote (SSH from the laptop), or plain SSH + Jupyter. For GPU work, the GPU is on the NAS; the laptop is the UI. Security: Tailscale or Cloudflare Access; never expose dev tools publicly. The setup is an afternoon; the benefit is years of "any device, same code."
Checklist
- ⬜ Install code-server on the NAS; access from a browser
- ⬜ Or: install VS Code Remote - SSH on the laptop; connect to the NAS
- ⬜ Configure the SSH config for daily use (with Tailscale)
- ⬜ Set up Jupyter on the NAS; forward the port via SSH
- ⬜ (If you have a GPU) install the NVIDIA drivers and CUDA; verify the GPU is available to notebooks
- ⬜ Configure a Git remote (GitHub, GitLab, Gitea) for the projects
- ⬜ Use Tailscale or Cloudflare Access for remote access; never expose dev tools publicly
Volume 5 is complete
The data scientist's NAS, Jupyter, data formats, pipelines, ML, sharing & collaboration, reproducibility, remote development. The data scientist's home lab is now a complete platform. Volume 6 is the last volume: the operations, the security, the disaster recovery, the runbook. The volume that turns "I built it" into "I can keep it running for years."