Overview
fjall tunnel opens an AWS Session Manager (SSM) port-forwarding tunnel to your application’s database through its bastion host. Connect local database tools (pgAdmin, DBeaver, psql) to a private RDS instance without exposing it to the public internet.
By default the tunnel is attached: it runs until you press Ctrl+C. With --background the SSM session keeps running after the command returns; fjall tunnel status lists the detached tunnels and fjall tunnel stop closes one.
Prerequisites
The
aws and session-manager-plugin binaries are checked before discovery starts. When either is missing, the command exits with Required tools not found: <list>. Install them before using tunnel.
Usage
Application name is required. Use: fjall tunnel <app-name>. There is no auto-detection and no application picker.
Arguments
Options
Basic usage
Open a tunnel to your application’s database:How it works
- Gates: checks your Fjall session and the project’s organisation binding.
- Prerequisites: checks that the AWS CLI and Session Manager plugin are installed.
- Bastion discovery: finds the bastion host instance in the application’s VPC.
- Database discovery: reads the application’s RDS endpoints from its stack outputs.
- Tunnel: starts an SSM port-forwarding session through the bastion to the database endpoint.
<engine> (<type>), for example postgresql (Instance). The engine is postgresql or mysql, and the type is Instance, Aurora or GlobalAurora.
Once connected, the tunnel prints its details and waits:
*** in every surface. Read the real credential from the database secret in AWS Secrets Manager.
Running in the background
--background starts the SSM session as a detached process in its own process group and returns once that session reports it is listening. The session outlives the command, the shell and the terminal:
<app>-<database> in lowercase letters, digits and hyphens. Fjall keeps a record for it under ~/.fjall/tunnels/<name>.json (the process id, local port, database endpoint, start time and log path) and writes the session’s output to ~/.fjall/tunnels/<name>-<session>.log. FJALL_CONFIG_DIR relocates both along with the rest of the CLI’s state.
The log belongs to the session rather than to the name: each start writes its own file, stamped with an id that is unique to that run, and opens it with a header line.
fjall tunnel stop removes the log of the session it ended, and leaves the log of one that had already died — which is where the reason usually is. The record is what names the file, so every command that reports a log path reports the exact one; run ls ~/.fjall/tunnels/ to see what a name has left behind.
One detached tunnel runs per app and database. Opening a second one while the first is alive is refused with Tunnel '<name>' is already running (pid <pid>, local port <port>). Run `fjall tunnel stop <name>` first. A record whose process has since died is removed first, and the command says so before it starts the new session:
fjall tunnel stop <name> clears it.
--background runs the same discovery steps as --non-interactive, so --database and --local-port apply and the database selection fails closed in the same way.
Nothing is left half-started. When the session cannot get to a listening state, Fjall stops it, removes its record and keeps its log:
fjall tunnel stop
SIGTERM, escalates to SIGKILL after a grace period, and then removes the record. The log goes with it only when this command is what ended a running session — a session that had already died keeps its log, because that is where the reason is:
tunnel stop never signals a process id that no longer belongs to a tunnel session, so a recycled pid is treated as “already gone” rather than killed.
fjall tunnel status
LISTENING is no when the process is alive but nothing answers on the local port yet, or any more. A pruned record’s log is kept so you can read why the session ended.
Neither stop nor status needs a Fjall session or AWS access: both work on local state only.
Non-interactive behaviour
--non-interactive prints the same four discovery steps as plain text and applies the selection flags. Database selection fails closed rather than guessing:
Agent mode
Agent mode refuses to start a tunnel without--background, returning the background_flag_required action with the exact command to rerun:
Ctrl+C, so agent mode only opens detached ones: the session keeps running after the command returns and the result names it. Where several databases exist, agent mode returns database_selection_required with one ready-to-run command per database instead of prompting.
The success payload carries name, localPort, target and status (detached) by default. Request pid, host, remotePort, engine or logPath with --fields.
fjall tunnel stop <name> --agent returns name, outcome (terminated, killed or already_stopped) and pid, with app, target and localPort on request. When several tunnels are running and no name was given it returns the tunnel_selection_required action with one fjall tunnel stop <name> command per tunnel.
fjall tunnel status --agent returns one row per detached tunnel (name, app, target, localPort, pid, listening; startedAt, remoteHost, remotePort and logPath on request) with a total aggregate, and logs one diagnostic per pruned record.
Connecting
Point any local client atlocalhost and the tunnel’s local port:
Ctrl+C when you finish, and a detached one with fjall tunnel stop.
Next Steps
fjall deploy
Deploy applications and infrastructure to AWS.
fjall list
List resources inside an application’s infrastructure.
fjall restore
Restore resources from AWS Backup recovery points.
fjall secrets
Manage application secrets backed by AWS Secrets Manager.