The addresses on this page are examples. Sign in on the booking page and this same manual shows this machine's real address, plus your own port.
Shoal User Manual
Shoal is the sharing platform for the lab's GPU machine. You get a container of your own, your own disk space, and a fixed SSH port. Only one person's container runs at a time; who gets which hours is decided by the slot table on the booking page.
There is only one thing the administrator has to do for you: add your account name and Gmail to the list. After that you paste your own key and provisioning happens automatically. You do not have to install anything.
What to prepare before you are provisioned
Generate an SSH key that belongs only to you
On your own computer:
ssh-keygen -t ed25519 -C "example@lab"
You will see
Generating public/private ed25519 key pair. Enter file in which to save the key (/Users/example/.ssh/id_ed25519): ← just press Enter Enter passphrase (empty for no passphrase): ← Enter, or set one Enter same passphrase again: ← just press Enter Your identification has been saved in /Users/example/.ssh/id_ed25519 Your public key has been saved in /Users/example/.ssh/id_ed25519.pub
Press Enter through the prompts (a passphrase is recommended but may be left empty). This produces two files:
~/.ssh/id_ed25519the private key; never send it to anyone~/.ssh/id_ed25519.pubthe public key; this is the one the administrator needs
Not sure what is inside the .pub file? Print it out and copy the whole line to send to the administrator:
cat ~/.ssh/id_ed25519.pub
You will see
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleExampleExampleExampleExampleExample example@lab ^^^^^^^^^^^ start ^^^^^^^^^^^ end The whole line, start and end included.
(On Windows PowerShell, use type $env:USERPROFILE\.ssh\id_ed25519.pub instead.)
That line looks like ssh-ed25519 AAAAC3Nza... example@lab. Copy the whole line, including the leading ssh-ed25519 and the trailing name, with no missing characters and no extra line breaks. The next step is pasting it into the system.
A second key, or a key that is not in ~/.ssh
The command above names no file, so the key lands in ~/.ssh/id_ed25519, which is a name ssh tries by itself; nothing to configure. If you already have other keys and want a separate one for this machine, pass -f with the full path, or it ends up in whatever folder you happened to be in and you will not find it again:
ssh-keygen -t ed25519 -f ~/.ssh/example -C "example@lab"
If the key is already somewhere else, move it into ~/.ssh and fix the permissions:
mv example example.pub ~/.ssh/ chmod 600 ~/.ssh/example
On Windows there is no chmod. Use icacls to strip inherited permissions and leave only yourself:
icacls "$env:USERPROFILE\.ssh\example" /inheritance:r /grant:r "$($env:USERNAME):(R)"
A private key must be mode 600, readable by you alone. With anything wider ssh refuses to use it and prints UNPROTECTED PRIVATE KEY FILE. Keys created with -f start out at 600, but copying, moving or bringing one over from another machine often loses that.
A key whose name is not one of the defaults is never tried automatically: put it in ~/.ssh/config (Connecting has the full alias; when going through the jump host, name it under both the jump host and the container, because -i on the command line only applies to the last hop). Without that you get a refused connection reading Permission denied (publickey), which looks exactly like having pasted the wrong public key.
You may not share a key with anyone else. The system enforces this at submission time: paste a key that already belongs to someone and it is refused the moment you press submit, not later during provisioning. A shared key means there is no separation of identity, nobody can tell who did what, and revoking one person means revoking everyone.
Sign in and paste your key
The administrator first adds your account name and Gmail to the list. Then:
- Open the booking page and click "Sign in with Google", choosing the Gmail the administrator registered.
- After signing in you will see "Account settings". Paste the single line you printed with
catinto "Your SSH public key" and submit.Before your key is registered, the full interface remains visible and only Book is disabled. "Account settings" stays open and pulses "SSH public key not registered".
Paste the whole line: it starts with ssh-ed25519 and ends with example@lab - The status changes to "provisioning". It is automatic and takes about a minute; this page updates by itself, showing your port number and enabling Book. There is nothing else to do and nobody to ask.
Left: "provisioning" right after you submit. Right: provisioned about a minute later, with your port number You will see
SSH public key not registered → you have not pasted one provisioning → pasted; about a minute to go provisioned → Book is enabled and "Your connection" below shows port 2299
To replace your key later, paste the new one in the same place; the old one stops working immediately.
Fallback: you may also send that .pub line directly to the administrator, who provisions you by hand.
Opening the booking page
Open https://ntcu-gpu-1.tail37e97f.ts.net/ from any network and sign in with the Google account the administrator registered. That is the normal way; you do not need to install anything.
You sign in with your Google account, not with your SSH key; the key is only used when connecting to the container. A sign-in lasts eight hours. After that you land back on the sign-in page; sign in again and nothing is lost.
On first sign-in, Project, the booking form, schedule and records remain visible before your key is registered. Add the key in the open "Account settings" panel; only Book stays disabled until provisioning finishes.
Booking
The booking page is the one you came here from. The booking form is on the left, the slot table on the right (you can page back and forward a week at a time), and your own accumulated record at the bottom. Four fields to fill in: project, date, start time, length, then pick a progress item. There is no user field — you are already signed in.
You will see
Booked #4 09/10 14:00 ~ 16:00
Those two cells on the right turn your colour; everyone else sees only "example" and the time.
When someone hands a slot back early, its cells keep the last user's name in a muted style and say "finished · open". This is a usage record, not a reservation; a new booking replaces it in the same cells.
| Rule | Value |
|---|---|
| Length of one slot | 2 hours, on fixed boundaries (00:00, 02:00, 04:00 and so on) |
| Most slots in one booking | 12, that is 24 hours |
| How far ahead you may book | 14 days |
| Minimum length of a progress name | 3 characters |
| Usage cap | None |
There is no cap on purpose: how long you ran, which project it was on, and whether the progress item you picked matches what actually ran on the GPU are all recorded and go into the weekly report.
Projects and progress items
A project is your own topic — your thesis, a study, a course project. Nobody creates it for you, and only you see your own list. Create one in the "My projects" box above the booking form; it only asks for a name (for example, example-project).
A progress item is one line of work you come back to across several slots ("finetune round 3", "dataset preprocessing"). Press "New" next to the progress field to add one; it is selected automatically once added. Pick one when you book and the hours accumulate against it, split by project and item in the "My record" panel at the bottom of the page.
The progress name is printed alongside the programs that actually ran on the GPU during that slot, so make it specific ("color model finetune round 3", not "experiments").
When booking is refused
- "Pick a registered project": an empty dropdown means you have no projects yet. Create one in the "My projects" box above the booking form (it opens automatically when empty); it only asks for a name.
- "Clashes with #N ...": that slot is taken. Pick another.
- "Say in one sentence what you are going to run": the progress name was shorter than 3 characters. Write something you will recognise later, not "experiment".
- "That progress item no longer exists": it was disabled, or you switched project without switching the item. Pick another one.
Connecting
After provisioning, the administrator gives you a port number. It is different for every person, and once assigned it never changes. Examples below use example as the account and 2299 as the port; replace example with your account and they are yours.
Put this in your ~/.ssh/config (replace example with your account and 2299 with your port):
Host example-gpu HostName gpu.example.lab Port 2299 User dev
Then:
ssh example-gpu
That address only resolves from inside the lab network.
Off campus, go through the jump host. The booking page gives you a ready-made block with your port already filled in. Paste it into ~/.ssh/config, then connect with one word: ssh example-gpu. It looks like this:
Host shoal-jump HostName ntcu-gpu-1.tail37e97f.ts.net Port 10000 User shoal StrictHostKeyChecking accept-new ProxyCommand openssl s_client -quiet -servername %h -connect %h:%p 2>/dev/null IdentityFile ~/.ssh/example Host example-gpu HostName 127.0.0.1 Port 2299 User dev StrictHostKeyChecking accept-new ProxyJump shoal-jump IdentityFile ~/.ssh/example
Do not delete the openssl line; the connection needs it. The 2>/dev/null at the end just hides the certificate chain openssl prints on every connection, which otherwise gets mixed into ssh's own output and looks like an error. Drop it if you are debugging a connection that will not come up.
What to install: nothing on macOS or Linux. On Windows, open PowerShell (ssh is built in, you do not need PuTTY), but install Git for Windows first to get openssl, then change the ProxyCommand line to the full path:
ProxyCommand "C:/Program Files/Git/usr/bin/openssl.exe" s_client -quiet -servername %h -connect %h:%p
It works during your slot, and keeps working after the slot ends as long as nobody is booked next and you have not been released. Outside that, the jump host accepts no key at all. If your network blocks port 10000, change the Port line to 8443.
Write both IdentityFile lines. A single -i only applies to the last hop, the jump host never sees it. If your key has the default name (~/.ssh/id_ed25519) you can leave both out. With the block in place, ssh, scp, rsync and port forwarding all just take the alias.
The StrictHostKeyChecking accept-new lines are what stop the first connection from asking you to accept a host key twice, once for the jump host and once for your container. It still refuses if a key you already trust ever changes.
You will see
Shoal jump host: access is granted only during your slot (including
overtime when nobody has booked after you). If you are refused, it is
usually not a key problem; check your slot on the booking page first.
The authenticity of host 'ntcu-gpu-1.tail37e97f.ts.net (203.0.113.10)' can't be established.
ED25519 key fingerprint is SHA256:ExampleJumpHostFingerprint0000000000000000.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes ← the jump host, first time
The authenticity of host '[127.0.0.1]:2299 ([127.0.0.1]:2299)' can't be established.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes ← your container, first time
your slot until 16:00 (1h 52m left)
workspace usage (updated 2026-09-08 14:08)
your data 12.0G
snapshots 1.4G
total 13.4G
(full details: quota)
GPU NVIDIA GeForce RTX 5090 1 MiB / 32607 MiB 0 % utilisation
dev@example:~$
Both addresses there are documentation examples; 203.0.113.10 comes from the reserved range in RFC 5737 and is nobody's machine. That three-line banner is printed on every connection, not only when you are refused; only the final Permission denied (publickey). line means you were refused:
Shoal jump host: access is granted only during your slot (including overtime when nobody has booked after you). If you are refused, it is usually not a key problem; check your slot on the booking page first. shoal@ntcu-gpu-1.tail37e97f.ts.net: Permission denied (publickey).
Writing ~/.ssh/config
Create the file if you do not have one, and keep its permissions tight; ssh refuses the whole file if they are too open:
mkdir -p ~/.ssh && touch ~/.ssh/config && chmod 600 ~/.ssh/config
On Windows PowerShell there is no mkdir -p, touch or chmod:
New-Item -ItemType Directory -Force ~\.ssh | Out-Null New-Item -ItemType File -Force ~\.ssh\config | Out-Null
Windows needs no 600: ssh there checks that the file belongs to you, which it already does inside your user folder.
One Host block is one alias. Once it is set, ssh, scp, rsync and port forwarding all take just the alias; no port, jump host or key to type again. A few things that actually bite:
- Do not reuse an alias you already have. With the same name twice the first one wins and you silently connect to a different machine. Check with
grep -n "^Host " ~/.ssh/configbefore choosing a name. - Through a jump host the key belongs in two places: the container’s Host block and the jump host’s. A
-ion the command line only applies to the last hop, never the jump; the symptom isPermission denied (publickey). - A Host block may list several
IdentityFilelines; ssh tries them in order. If you have two accounts on the same machine, list both under the jump host. - Adding
IdentitiesOnly yesrestricts ssh to the keys you listed. Worth it if you have many keys, so ssh does not burn its attempts on the wrong ones before the server cuts it off. ProxyCommandonly works inside the config file, not as a command-line-o. A-o ProxyCommand=…on the command line applies to the last hop, which the jump host never sees, so there is no one-line version of the off-campus command.
Check what ssh actually resolves to (these are the values it will use, not the ones you think you wrote):
ssh -G example-gpu | grep -E "^(hostname|port|user|proxyjump|proxycommand|identityfile)"
On Windows PowerShell there is no grep:
ssh -G example-gpu | Select-String '^(hostname|port|user|proxyjump|proxycommand|identityfile)'
One bad line breaks every alias in the file; the error names the line number. For instance Match has no port condition, so writing one gives Unsupported Match attribute port and the whole file is rejected. After editing, run that ssh -G line: no error means it loads.
Inside, you are dev, with no sudo. The container is itself the isolation boundary, so just pip install whatever you need.
Working inside the container
Three lines to read first
The login banner prints three things: how long your slot has left, how much of the workspace you use, and what the GPU is doing. Later: quota for disk, nvidia-smi for the GPU, htop for CPU and memory.
Each container is capped at 16 CPU cores, 20 GB of RAM and the whole GPU (32 GB of VRAM); disk is shared by the whole machine.
A process that goes past 20 GB of RAM is killed outright (cgroup OOM); all you see is Killed. That is not a broken machine: use a smaller batch, load data in chunks, or write intermediate results to disk.
Where files go
Everything goes in /workspace: code, datasets, models, checkpoints, logs. Four folders are enough:
/workspace/code source (git clone here) /workspace/data datasets /workspace/models model weights /workspace/runs per-run outputs, checkpoints, logs
The home directory (~) is for .bashrc, .gitconfig and the like; a rebuild wipes it. Treat anything outside /workspace as gone at any moment.
Moving files in and out
From your own machine, use the alias you put in ~/.ssh/config under Connecting (off campus that is the one with ProxyJump); port and jump host live in the alias, so the commands stay short. Single files with scp:
scp file example-gpu:/workspace/
You will see
dataset.tar 100% 1.2GB 38.4MB/s 00:32
Whole folders with rsync; it resumes after interruptions and only copies what changed:
rsync -avP folder/ example-gpu:/workspace/data/folder/
Swap source and destination to copy out. Both only work while you can reach the container (during your slot, or while it keeps running afterwards).
Windows has no rsync, and Git for Windows does not ship one. Copy folders with scp -r:
scp -r folder example-gpu:/workspace/data/
It does not resume and does not copy only the differences, so an interruption means starting over. Do not push large datasets from your own machine: download them inside the container instead (next section), where the network is much faster than yours.
Public datasets and models are best downloaded inside the container; the machine's link is far faster than yours. wget and curl -LO are there. For Hugging Face use the official tool:
pip install -U "huggingface_hub[cli]" hf download Qwen/Qwen2.5-1.5B-Instruct --local-dir /workspace/models/qwen2.5-1.5b
Installing packages
Python packages: just pip install. On an interactive login /workspace/.venv is already active, so what you install lives with the workspace and survives a rebuild. torch is already in it (the image's build, which supports this card). Do not install another torch: it pulls several GB and the version you get most likely does not know the RTX 5090.
System packages: you have no sudo, so apt does not work; that is deliberate. The image ships with git, vim, less, curl, wget, rsync, unzip, tmux, htop, cmake, gcc and the full CUDA 13.2 toolkit (nvcc). If you really need something else, give the package name to the administrator; it goes into the image and everyone gets it.
Anything long runs in tmux
A program started straight from the shell dies with the SSH connection: a network blip, a closed laptop lid, your end dropping while the jump host is still fine. So anything that takes more than a few minutes goes into tmux:
tmux new -s train python train.py 2>&1 | tee /workspace/runs/train-$(date +%m%d-%H%M).log
Press Ctrl-b, release, then d to detach; the program keeps running. Reattach with tmux attach -t train; list sessions with tmux ls.
tmux sessions live inside the container; when the container is stopped (handover at the end of a slot, a maintenance rebuild) they are gone. Anything that spans slots relies on checkpoints, not on tmux.
Checkpoint often
At handover the container is stopped with docker stop -t 120: your program gets SIGTERM and two minutes before it is killed. Save a checkpoint every fixed number of steps to /workspace/runs/ and make the training resumable from it; then being stopped costs a few minutes at most, and the next slot picks up where you left off.
VS Code (the easiest way to work with notebooks)
Install the Remote - SSH extension, press F1, choose Remote-SSH: Connect to Host, and pick example-gpu from the list. It reads the same ~/.ssh/config you already wrote, so there is nothing else to set up.
Once connected, open /workspace as a folder, drag files straight into it, and run .ipynb notebooks in the editor with this card behind them. For most people this replaces both scp and setting up Jupyter by hand. The connection counts as you being connected, so it does not mark you a no-show.
Jupyter
Install it in the container and bind it to localhost only:
pip install jupyterlab jupyter lab --no-browser --ip 127.0.0.1 --port 8888
In a second terminal on your own machine, forward the port (this connection opens no shell, but it still counts as you being connected, so you are not marked a no-show):
ssh -N -L 8888:localhost:8888 example-gpu
Open http://localhost:8888 in the browser; the token is on the line Jupyter printed. Notebooks go in /workspace like everything else.
Building CUDA code (llama.cpp as the example)
nvcc is on the PATH, CUDA 13.2. Do the whole thing inside /workspace so the build survives:
cd /workspace git clone --depth 1 https://github.com/ggml-org/llama.cpp cd llama.cpp cmake -B build -DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES=120 cmake --build build --config Release -j 16
CMAKE_CUDA_ARCHITECTURES=120 is the RTX 5090's architecture; building only that one takes about 3 minutes on 16 cores. Models go in /workspace/models:
mkdir -p /workspace/models && cd /workspace/models wget https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct-GGUF/resolve/main/qwen2.5-1.5b-instruct-q4_k_m.gguf
Benchmark (-ngl 99 puts every layer on the GPU):
/workspace/llama.cpp/build/bin/llama-bench -m /workspace/models/qwen2.5-1.5b-instruct-q4_k_m.gguf -ngl 99
Reference numbers for this model on the RTX 5090: prompt processing (pp512) about 40000 tokens/s, generation (tg128) about 700 tokens/s. If you are far below that, check nvidia-smi for something else on the GPU first. To serve it, llama-server -m model -ngl 99 --port 8080, then forward 8080 the same way as Jupyter above.
Is the GPU actually working
When something feels slow, measure a matrix multiply first; bf16 on the 5090 should be above 200 TFLOPS:
python -c "import torch,time;a=torch.randn(8192,8192,device='cuda',dtype=torch.bfloat16);a@a;torch.cuda.synchronize();t=time.time();[a@a for _ in range(20)];torch.cuda.synchronize();print(f'{2*8192**3*20/(time.time()-t)/1e12:.0f} TFLOPS bf16')"
If that number is right and your program is still slow, the problem is in the program (data loading, CPU preprocessing, a tiny batch), not the machine.
When your slot is about to end
You get a notice 15 minutes before the end (a wall message in the terminal, and /workspace/.booker-notice). Do three things: save a checkpoint; make sure everything you want to keep is in /workspace, or rsync it out; and when you are done press "Done, hand it over" on the page or run release in the container. If nobody follows you the container keeps running, but the moment someone books you are stopped; do not treat that extra time as yours.
What happens when your slot arrives
Your slot starting does not mean the previous person's experiment gets killed. This is the most important rule in the system.
If your slot arrives while the previous person's GPU work is still running:
- The system tells you the slot is being deferred, and tells them that someone is waiting and they should wrap up
- The GPU has to stay idle for 5 consecutive minutes before their experiment counts as finished
- Then a further 10 minutes of notice
- Only then is their container stopped, and with
docker stop -t 120, giving scripts two minutes to write a checkpoint
The other way round, when it is you that someone is waiting for, step 3 is a message addressed to you: wall puts it on your terminal, and the same line is appended to /workspace/.booker-notice.
You will see
[09/08 14:08] GPU idle for 5 min; your job is considered finished. Handing over to example2 in 10 min. If you still need to run, start working again now.
Someone running over is never killed automatically. After 120 minutes of overrun the system only raises an alert asking a human to step in.
Rules that can cost you a slot
- No connection within 30 minutes of the slot starting counts as a no-show
- Nobody connected and no GPU work for 30 consecutive minutes releases the slot (bookings longer than 24 hours are exempt and kept until they end)
- You get a reminder 15 minutes before the slot ends
When you are finished
Hand the slot back yourself. While your slot is running, the booking page shows a "Done, hand it over" button. Pressing it releases the slot immediately so the next person does not have to wait.
If GPU work is still running, pressing it does not stop anything: the page tells you how many jobs are still on the GPU. Stop them yourself, or let them finish, then press again. There is no forced stop from the web page, so one stray click cannot kill your own half-finished run.
If nobody is booked after you, your container keeps running when the slot ends and this button stays. The status line says the slot ended, nobody is next, and it is still running. That is not a free slot: hand it over when you are done.
If you are already at the container's terminal, running release there hands the slot back too, in fewer steps than opening a browser (add release --force if the GPU is still busy). It takes effect within about a minute; on success the container is stopped and your SSH session drops — that is the confirmation. If it cannot release (for example the GPU is still busy and you did not pass --force) it prints why. --force only skips the "GPU is still busy" check; it only ever touches your own slot and your own container, and if the current slot is not yours it simply tells you that you have no slot running. There is no full booker command inside the container, only release.
What survives and what does not
| Location | After the container is rebuilt |
|---|---|
/workspace | Survives |
/workspace/.venv | Survives |
| Home directory, anything installed with apt, anywhere else in the container | All gone |
/workspace/.venv is a Python environment created automatically on first start. It inherits torch from the image, so you do not have to reinstall several gigabytes. It is on the PATH, an interactive login activates it automatically, and non-interactive use such as ssh example-gpu "python train.py" picks it up too. Install anything you intend to keep into it, so that it travels with the workspace.
Disk
There is no per-person quota. Usage is recorded, not capped. Everyone shares one pool, and the administrator is alerted when it gets full.
The number df -h /workspace shows is the shared free space, not your personal allowance.
Deleting files does not give the space back immediately. Older snapshots still pin that data until they rotate out. So "I deleted a lot and still cannot write" is expected, not a fault.
Recovering files from a snapshot
If you delete something by accident you can get it back yourself, without asking anyone:
ls /workspace/.zfs/snapshot/ cp -r /workspace/.zfs/snapshot/<some-snapshot>/the-file-you-want /workspace/
You will see
auto-2026-09-08-0500 auto-2026-09-08-0600 auto-2026-09-08-0700 The names are UTC, eight hours behind Taiwan: auto-2026-09-08-0500 is the 13:00 snapshot on 9/8 local time.
ls /workspace does not show the .zfs directory, but you can enter it by typing the full path. Snapshot names are in UTC, eight hours behind Taiwan time, so do not mistake a recent one for an old one.
How the system reaches you
When the system has something to tell you it uses two channels: wall inside the container (which you see if a terminal is open), and a line appended to /workspace/.booker-notice. If you missed the live message:
tail /workspace/.booker-notice
What the system records
To prevent accounts from being lent out, and to find the cause when something goes wrong, this machine records: the IP address, country, network provider and account of every web and SSH login, the programs and commands you run in your container, which account is using the GPU, and the container's outbound network connections (destination IP and port, never the content). Only administrators can see these records, and they are kept for 180 days and then deleted automatically.
Running relay services that expose the container to the outside (ngrok, cloudflared, frp, ssh -R / ssh -D and the like) is prohibited, and so is lending your account to anyone else. The system notifies the administrator when it detects one, and violations lead to suspension. ssh -L back to your own computer (for Jupyter or TensorBoard) is fine.
When something is wrong
Look up the message on your screen; do not guess at a fix. Every entry below actually happened to a user of this machine, and the messages are quoted verbatim.
One thing that helps with all of them: when a connection fails, run it again as ssh -vvv example-gpu. It prints which config file it read, whether it managed to start openssl, and where it stopped. When you ask someone for help, that output is worth far more than "it does not connect".
Permission denied (publickey)
Nine times out of ten this is not about your key. You are outside your slot. The jump host accepts your key only during your own slot; outside it, it accepts nobody.
Check the booking page for whose slot it is right now. Three common cases:
- You never booked. Book a slot, wait about a minute for the container to start, then connect
- You booked, but nobody connected within the first 30 minutes, so the slot was released as a no-show. It shows as no-show on the page; just book another one
- Your slot has ended and somebody else booked the one after it
If you really are inside your slot and still get refused, then it is about the key: check that the public key you pasted into the system is the one on this machine. Compare the fingerprint from ssh-keygen -lf ~/.ssh/id_ed25519.pub with the one shown in "Your connection" on the booking page.
CreateProcessW failed error:2 or posix_spawnp: No such file or directory
Windows only. This means openssl.exe could not be found. It is not an SSH problem, and the message does not tell you which file is missing.
Windows has no built-in openssl; it comes with Git for Windows, and that is not always installed in C:\Program Files\Git. The easiest fix is the Windows one-paste block in "Your connection" on the booking page, which finds the path for you. To find it by hand:
$git = (Get-Command git).Source $root = Split-Path (Split-Path $git) Join-Path $root 'usr\bin\openssl.exe'
Put the path it prints inside the quotes on your ProxyCommand line. If you do not even have git, install it first: winget install --id Git.Git -e.
s_client: cannot provide both -connect option and target parameter
Windows only. Your ProxyCommand line has something after %p, usually a redirect copied from somewhere else.
Windows OpenSSH runs ProxyCommand without going through cmd.exe; it starts the program directly. So a redirect is not treated as a redirect, it is handed to openssl as an argument. The line has to end at %p:
ProxyCommand "C:/Program Files/Git/usr/bin/openssl.exe" s_client -quiet -servername %h -connect %h:%p
Once it is right, openssl prints a few certificate lines on every connection (Connecting to ..., depth=3 ..., verify return:1). That is normal output, not an error, and your container prompt follows it.
config line 5: unsupported option "accep" (or any other truncated word)
A typo in your config file. ssh tells you the line number and the word it could not understand, but not the correct spelling.
Do not retype it. Use the Windows one-paste block on the booking page to rewrite the whole config, or copy the block from "Your connection" in one piece instead of typing it out.
It asks Are you sure you want to continue connecting (yes/no)? and you cannot type
This happens on Windows whenever a ProxyCommand is involved: ssh wants to read your answer from the console, but the console is held by the openssl child process, so your keystrokes go nowhere and the connection eventually times out.
Do not fight it, remove the question. Add one line to each of the two Host blocks in your config:
StrictHostKeyChecking accept-new
It means "record a host key you have never seen before", and it still refuses if a key you already trust ever changes, which is the same trust you were applying by typing yes without checking. The block the booking page gives you already contains both lines.
Connection closed by UNKNOWN port 65535
On its own this line says nothing useful. It means the ProxyCommand program died. The real reason is in the lines above it: scroll up to see what openssl printed, then match it against the entries above.
kex_exchange_identification: Connection closed by remote host
You reached the other end, but it closed the connection before the handshake. Usually a wrong port, or a ProxyCommand line that was deleted or mistyped: the jump host's port is a TLS endpoint, and a bare connection without openssl wrapping it is dropped immediately.
REMOTE HOST IDENTIFICATION HAS CHANGED
Work out which case you are in first. Everyone's container is reached at 127.0.0.1:2299, so "the host key for that address" on your machine can easily be pointing at something else:
- You have connected to another machine before, or the machine was reinstalled, or that port used to belong to somebody else: this is the common case, just a stale record. Remove that one entry and connect again; you will be asked to accept the new host key once, answer yes:
ssh-keygen -R "[127.0.0.1]:2299"
The same command works on Windows. - None of the above (same machine all along, the port has always been yours): do not clear it, ask the administrator. A container's host key is stored outside the container and survives a rebuild, so in that case a change is worth investigating.
Either way, never delete the whole known_hosts file: that throws away the records for all your other machines too.
UNPROTECTED PRIVATE KEY FILE
The permissions on your private key are too open, so ssh refuses to use it.
chmod 600 ~/.ssh/id_ed25519
On Windows use icacls:
icacls "$env:USERPROFILE\.ssh\id_ed25519" /inheritance:r /grant:r "$($env:USERNAME):(R)"
Not a connection problem
- Google sign-in is refused, saying you are not on the list or the account is unknown: either the administrator has not registered your Gmail yet, or you picked a different Google account. Check you signed in with the Gmail the administrator registered, then ask them about the list
- The booking page does not load at all (no response, or it spins forever): that is the scheduler or the machine itself, not your side. Ask the administrator
- The booking page works but
sshdoes not connect: usually your container is not running. Containers only run during your slot; that is the design, not a fault
If none of this applies, ask the administrator and give them both the command you ran and the full ssh -vvv output.