What you are putting together
The goal is simple to state: you open a browser tab on your laptop, and the notebook in it trains on a GPU in a data center. Four pieces make that work. The NVIDIA driver lets the operating system talk to the card. PyTorch, installed with CUDA support, lets your Python code use it. JupyterLab serves the notebooks. An SSH tunnel carries JupyterLab from the server to your laptop.
Jupyter's security documentation explains why the tunnel is not optional: reaching a Jupyter server means being able to run arbitrary code on it. That is the whole point of a notebook, and it is why you never want strangers able to load the login page.
How big does the server need to be?
Start with the card's memory, because a model that does not fit simply refuses to load. Then check system memory and disk. These are the questions to answer before you order:
| Ask yourself | How to work it out |
|---|---|
| Does my model fit on the card? | Multiply the parameter count by two bytes for 16-bit weights, one for 8-bit, or half a byte for 4-bit. Then allow roughly 25-50% extra on top for activations and the runtime. |
| How much RAM for a team hub? | Most people active at once times the most one person uses, plus 128 MB (the TLJH rule). |
| How much disk for a team hub? | Everyone with an account times the most one person stores, plus 2 GB (also TLJH). |
| How much RAM just for me? | Enough for the largest dataset you load into memory, with room to spare. |
The TLJH guide suggests running through one typical session, measuring it, and adding 20-40% so people have room to experiment. If someone goes past their allowance, their kernel restarts rather than the whole server falling over.
Why rent the whole server
With a GPU server from Primcast, the card goes into a physical machine rented to you alone and is handed straight to your operating system. Nobody else's processes run on that card, and no partitioning or turn-taking layer sits between your code and it, so a neighbour's batch job is never the reason your experiment slowed down. You install your own driver and CUDA release and keep them pinned for as long as your notebooks need them.
- A screen when things go wrong. The GPU machines include the server's own HPE iLO or Dell iDRAC, so if a driver update takes the network down you can still see the console, mount an ISO and cycle power.
- No meter on data. Pull datasets in and push checkpoints out as often as you like. Your traffic is never metered and no egress fee ever lands on the invoice.
- No lock-in. Billing is monthly with no contract. Upgrade memory, disks or the port on the machine you already have.
Delivery depends on the card: about 30 minutes once approved if a machine already carries it, 4-24 hours when a card we hold is fitted, and longer when one has to be bought in. Use build to order to add disk space or memory to the entry build, and the data centers page to pick the city nearest you.
Up and running in an evening
- Bring Ubuntu up to date and reboot.
- Run
ubuntu-drivers list --gpgputo see the server drivers for your card, thenubuntu-drivers install --gpgpu. Ubuntu's guide also adds thenvidia-utils-…-serverpackage with the same branch number, which is wherenvidia-smicomes from. Reboot and check thatnvidia-smilists the card. - Make a Python virtual environment for your notebook work.
- Install PyTorch. On a Turing card or anything newer (RTX 20 series onwards, T4, L4, L40S, A100, H100, the Blackwell RTX 5080, 5090 and RTX PRO 6000) the plain
pip install torch torchvisionis right; it is a CUDA 13 build and wants a 580-series driver or newer. Older generations are left out of PyTorch's CUDA 13 builds: that means the V100 and TITAN V (Volta), the P100, P40, P4 and GTX 10-series cards (Pascal), and the Tesla M60 and M6 (Maxwell). For those, use the commented line below, which pins PyTorch 2.14, the final version PyTorch publishes with CUDA 12.6 wheels. The Kepler K3100M is too old for any current PyTorch. - Install JupyterLab next to PyTorch and ask Python whether it can see the card.
sudo apt update && sudo apt upgrade && sudo reboot
sudo ubuntu-drivers list --gpgpu
sudo ubuntu-drivers install --gpgpu
sudo apt install nvidia-utils-<branch>-server # branch from the list, e.g. 580
sudo reboot
nvidia-smi
sudo apt install python3-venv
python3 -m venv ~/notebooks-env
. ~/notebooks-env/bin/activate
pip install torch torchvision # Turing or newer, driver 580+
# Maxwell / Pascal / Volta use this line instead:
# pip install torch==2.14.0 torchvision==0.29.0 --index-url https://download.pytorch.org/whl/cu126
pip install jupyterlab
python -c "import torch; print(torch.cuda.is_available()); print(torch.cuda.get_arch_list())"
The first line printed should say True. The second lists the GPU generations this PyTorch build was compiled for; your card's should be among them.
Opening JupyterLab without opening a port
Out of the box, Jupyter Server only listens on localhost and asks for a token it prints when it starts. You keep both. Start JupyterLab on the server (inside tmux, so closing your terminal does not stop it), then let SSH forward the port to your laptop.
# server
. ~/notebooks-env/bin/activate
jupyter lab --no-browser --ip=127.0.0.1 --port=8888
# laptop: -L forwards the port, -N skips the remote shell
ssh -N -L 8888:127.0.0.1:8888 you@your-server
# then paste the 127.0.0.1:8888 link with ?token= from the server's output
Your firewall only ever needs SSH open. Tired of tokens? jupyter server password stores a hashed password instead. Already running Jupyter on your laptop's 8888? Forward a different local port, such as 8890, to the server's 8888.
Never blank out both the token and the password to make logging in easier. Jupyter's docs say that switches authentication off and is not recommended unless something else controls access.
When your team wants in too
Do not hand colleagues your tunnel. Jupyter's guide to a public server is clear that it is meant for one person, and several people in one server overwrite each other. JupyterHub fixes that by giving everyone a login and their own notebook server. The Littlest JupyterHub (TLJH) is the one-machine version: it supports current Debian and Ubuntu LTS releases, needs root, and gives all users one shared conda environment, so you install PyTorch once for everybody with sudo -E pip install from an admin terminal.
Install it with the bootstrap command from the TLJH guide (curl -L https://tljh.jupyter.org/bootstrap.py | sudo -E python3 - --admin yourname), then turn on HTTPS once a domain name points at the server: tljh-config takes your email and domain, and after a proxy reload TLJH requests a certificate from Let's Encrypt and keeps renewing it for you.
TLJH's default login lets each account choose its password the first time it signs in, admin included. Whoever gets there first sets it. So leave ports 80 and 443 shut while it installs, and claim the admin account privately: open a tunnel from your own computer, ssh -N -L 8001:127.0.0.1:80 you@your-server, browse to http://127.0.0.1:8001 and choose a strong password. Keep the web ports closed until the moment you turn HTTPS on, create your colleagues' accounts after that, and ask each of them to sign in straight away.
Everyone's notebooks share the one card's memory, and this setup does not ration it per person. PyTorch also holds on to memory it has freed, so nvidia-smi can look full when it isn't. Ask people to call torch.cuda.empty_cache() or shut down kernels they are not using.
Keeping it healthy
- Put notebooks in git and copy trained checkpoints somewhere else. Mirrored disks protect you from a dead drive, not from
rm. - Once a driver and PyTorch pairing works, hold it. Upgrade on purpose, between jobs, with the console handy.
- If one card stops being enough, talk to us about a multi-GPU build; those are priced individually.
- If you only need a GPU now and then, a monthly server will mostly sit idle; it suits steady, ongoing work.
When the notebook phase ends and you want to serve a model to an app, our vLLM server guide picks up from there.
Questions people ask
- Is it safe to put JupyterLab on the public internet with a password?
- Jupyter does document it: a hashed password, a TLS certificate and the server bound to every interface. It stays a one-person setup, though, and your login page becomes visible to anyone scanning. The SSH tunnel avoids all of that, so use it unless you have a strong reason not to.
- Do I have to install the CUDA toolkit separately?
- Not to use PyTorch from pip. The selector on the install page asks which CUDA version you want and hands you an index URL to install from, with the NVIDIA driver already on the machine. The full toolkit only appears in the instructions for building PyTorch yourself.
- Can my older card still run PyTorch?
- Volta, Pascal and Maxwell cards can run PyTorch up to version 2.14, as long as you install
torch==2.14.0withtorchvision==0.29.0from the cu126 index. The default CUDA 13 build skips those generations, and PyTorch has said it publishes no CUDA 12.6 wheels after 2.14, so newer versions mean compiling PyTorch yourself against a CUDA 12 toolkit. After installing, runtorch.cuda.get_arch_list()and look for your card's generation. Kepler cards will not work with any current release. - How soon can I start working?
- About 30 minutes after approval if a machine with your card is already racked, and 4-24 hours if a card we hold has to be fitted. Cards that must be bought in take longer; the build to order page says which applies.
- What happens if I break the driver?
- The server's own management console, iLO or iDRAC, sits apart from its network and still works. From there you can see the screen, roll back the driver or reinstall from your own ISO.