Appearance
Shell and files
Open a shell in a running service from the dashboard or the CLI, manage the files in a service's volume without a shell at all, or connect to the volume with an SFTP client.
All of them need the member role (viewers can't use them). Every shell session, every change to volume files and every SFTP server start and stop is recorded in the team's activity log.
Terminal
The Terminal tab of a service opens a shell in one of its running replicas: bash if the image has it, otherwise sh. It runs in the app's container, as the container's user, with the service's variables in the environment.
- With more than one replica, pick the one to connect to from the list next to the status.
- Reconnect opens a new session. If the connection drops (a network blip, a Fjordrun update), the terminal reconnects by itself a few times.
- A session closes after 30 minutes without input, and after 4 hours in any case.
- A shell doesn't count as traffic: a service that sleeps when idle can fall asleep, and end the session, while you're in it.
Anything you change outside a volume is lost on the next deployment or restart: each replica starts from the image. Use the terminal to inspect, debug and run one-off tasks (a migration, a script), not to change the app.
From the CLI
bash
fjordrun ssh # interactive shell in the linked service
fjordrun ssh --service api # another service of the linked project
fjordrun ssh -- ls -la /data # run one command
fjordrun ssh --replica web-7d9c-x2k4q # a specific replica
cat dump.sql | fjordrun ssh --service postgres -T -- psql -U postgresThe command's arguments are joined with spaces and run with /bin/sh -c, like ssh does. fjordrun ssh exits with the command's exit code, so it works in scripts and CI (with a team API token in FJORDRUN_TOKEN). When stdin or stdout isn't a terminal, or with -T, no terminal is allocated: input is forwarded until it ends, and stderr stays separate from stdout.
When there is no shell
- No running replica: a stopped service, a service that is asleep (open its URL to wake it), or a cron job between runs. The terminal tells you which.
- Images without a shell (distroless,
scratch): there is no/bin/shto start, so the terminal shows This image has no shell. Use the file manager for the volume, or build an image that includes a shell (for example a-debugor-slimvariant of the base image). - Agent services run in a gVisor sandbox. The shell works the same way there.
Files
Services with a volume have a Files tab: a file browser for the volume.
- Click folders to open them; the path at the top takes you back.
- Upload files, or drag and drop them onto the list. Each file can be up to 100 MB. Uploading over an existing file asks first.
- Download, rename (type a new name; a name with
/moves the entry) and delete (with a confirmation; deleting a folder deletes everything in it) from each row.
Paths are relative to the volume: / is the folder the volume is mounted at (for example /data). You can't leave the volume, also not through symbolic links: links pointing outside the volume are listed but can't be opened.
The file manager works with every image, including images without a shell, and also while the service is stopped or asleep. Files you upload belong to the owner of the folder they are uploaded to, so the app can keep writing them.
Databases
The volume of a database holds its data files. Changing or deleting them while the database runs can corrupt it. Use the database's own tools for backups and restores (see Databases).
From the CLI
bash
fjordrun files ls /uploads
fjordrun files get /backups/db.sql ./db.sql
fjordrun files get /config.json - # to stdout
fjordrun files put ./seed.json /seeds/ # a path ending in / is a folder
fjordrun files put --force ./seed.json /seeds/seed.json
fjordrun files mkdir /exports
fjordrun files mv /exports /archive/exports
fjordrun files rm /tmp-import # asks first; --yes to skipSFTP
For bigger transfers, or to use the tools you already have, start an SFTP server for the volume and connect with FileZilla, WinSCP, Cyberduck or the sftp command. It is available on the Hobby plan or higher and needs the member role.
SFTP is key-based only: there are no passwords. Add your public key under Account → SSH keys first (see SSH keys); without one, Start SFTP is disabled and links you there.
Start a server
Open the service, then the Files tab, and choose Start SFTP.
After a few seconds the card shows the host, port, user and the server's host key fingerprint, and a ready-made command:
bashsftp -P 20345 fjordrun@tcp.fjordrun.appConnect. The volume is the
/filesfolder.
The server accepts the keys your account has when you start it. A key you add later works the next time you start a server; deleting a key stops the servers that were started with it. Only the person who started the server can log in: starting one on a shared service doesn't give your teammates access with their keys.
The server stops by itself after 15 minutes without a connection (connected clients keep it running), and after 8 hours at the latest. Choose Stop SFTP to stop it earlier. Starting it again takes a few seconds. The port is different each time, the host key stays the same.
With the CLI:
bash
fjordrun ssh-keys add ~/.ssh/id_ed25519.pub
fjordrun sftp # starts the server and prints the sftp command
fjordrun sftp stopCheck the host key
On the first connection your client shows the server's host key fingerprint. Compare it with the one in the dashboard (or fjordrun sftp) before you accept it: it is the same for every session of the service, so a different one means you are not talking to your volume.
text
$ sftp -P 20345 fjordrun@tcp.fjordrun.app
The authenticity of host '[tcp.fjordrun.app]:20345' can't be established.
ED25519 key fingerprint is SHA256:Qm2… <- must match the dashboardClients
- sftp —
sftp -P <port> fjordrun@tcp.fjordrun.app. Use-i ~/.ssh/other_keyfor a key that is not one of your default ones. Recentscpversions transfer over SFTP too (scp -P <port> file fjordrun@tcp.fjordrun.app:/files/); the server has no shell, sosshandrsyncover SSH don't work. - FileZilla — File → Site Manager → New site. Protocol SFTP - SSH File Transfer Protocol, the host
tcp.fjordrun.app, the port from the dashboard, logon type Key file, userfjordrun, and your private key file (FileZilla converts OpenSSH keys to its own format the first time). - WinSCP — File protocol SFTP, host name
tcp.fjordrun.app, the port, user namefjordrun, then Advanced → SSH → Authentication → Private key file. - Cyberduck — Open Connection, SFTP (SSH File Transfer Protocol), the server, port and user, and choose your private key under SSH Private Key; leave the password empty.
What the server allows
Only file transfer. There is no shell or terminal, no port forwarding, no agent or X11 forwarding and no way out of the volume: the session is confined to a folder that contains nothing but your volume, so other files of the container, the cluster and other services are not reachable. Files you upload belong to the same user and group that own the volume, and uploaded files get group write permission within about 30 seconds (SFTP clients usually create files as 644), so the app can keep writing them. Run chmod from your client to change a file's mode afterwards.
Deploys stop SFTP
A volume can only be attached to one server at a time, so deploying the service, or removing its volume, stops its SFTP server. Start it again afterwards.
How it works
The shell is a kubectl exec-style session into the running container: nothing is installed in your image, and no SSH server or port is opened.
The file manager doesn't need anything in your image either. It adds a small helper container (BusyBox) to a running replica, which mounts the same volume. When nothing runs, a short-lived helper pod mounts the volume instead. It stops after 15 minutes, and as soon as the service is deployed or started. A volume can only be attached to one server at a time: a service woken from sleep while the helper pod holds its volume may wait until the helper stops.
The SFTP server is a small separate pod (OpenSSH with only the internal-sftp subsystem, 0.1 vCPU and 128 MB at most) that mounts the volume, on the same server as your running replicas, because a volume can only be attached to one server at a time. It has no internet access and is reached through the same public TCP host as database ports, on a port of the same range; the port is released when the server stops.
Pricing
The terminal, the file manager and SFTP cost nothing extra. Commands you run in a replica use its CPU and memory, which are billed as usual. The helper pod used while a service is stopped and the SFTP server are not billed (the volume itself is, as always).
API
The API reference lists the file manager endpoints (/services/{id}/files…), the SFTP endpoints (POST, GET and DELETE /services/{id}/sftp, returning an SFTP server) and POST /services/{id}/shell/ticket. Your SSH keys are managed with /auth/ssh-keys (SSH key). The shell itself is a WebSocket:
POST /api/v1/services/{id}/shell/ticketwith a bearer token returns a one-time shell ticket, valid for 30 seconds. Optional body:{"replica": "…", "command": "ls -la", "tty": true}.- Open
wss://api.fjordrun.com/api/v1/services/{id}/shell?ticket=…&cols=120&rows=40(the ticket'surl, plus the terminal size). The ticket is the authentication: browsers can't send headers on a WebSocket.
Messages in both directions:
| Frame | Direction | Content |
|---|---|---|
| binary | client → server | 0x00 followed by stdin bytes |
| binary | server → client | 0x01 + stdout bytes, or 0x02 + stderr bytes (with a terminal, all output is stdout) |
| text | client → server | {"type": "resize", "cols": 120, "rows": 40}, {"type": "stdin", "data": "ls\r"}, {"type": "eof"} |
| text | server → client | {"type": "ready", "replica": "…", "replicas": […]}, {"type": "exit", "code": 0}, {"type": "error", "message": "…"} |
The server closes the connection after exit (code 1000) or error: 4401 for an invalid, expired or used ticket, 4404 when the replica is gone, 4408 after the idle or maximum session time, 4429 with too many open sessions (10 per user), 4500 when the shell could not start or the connection to the replica ended without an exit code (for example, the replica was replaced mid-command).