Configuring NetBeans for Remote Debugging over SSH
Many Java teams in Australia run staging or production-like environments on machines far from their desks. A developer in Sydney might step through code executing on a Linux host in Adelaide, while someone in Brisbane debugs a microservice on a server in Perth. NetBeans handles that distance well when wired to the remote JVM through a secure tunnel. SSH is the most reliable carrier because it requires no extra firewall rules and rides on infrastructure local shops already trust.
Remote debugging is a daily tool for engineers supporting legacy enterprise systems in Australia. Banks in Melbourne, telcos in Canberra, and government departments in Hobart often keep application servers behind tightly locked-down networks. The standard practice is to bounce the debugger session over SSH rather than opening JDWP ports to the internet, keeping the attack surface narrow.
Before touching any settings, make sure the host is reachable. A quick sanity check is ssh user@host echo OK in a terminal. If you are behind Australia's National Broadband Network and your ISP uses carrier-grade NAT, you may need to be on the office VPN or use a jump host. Working from Darwin or Cairns means higher latency, so plan breakpoints accordingly.
Readers already comfortable with the basics can skip to the comparison table below. For broader context on the IDE and related tooling, the netbeans-blog.org homepage collects recent tutorials alongside more specialised guides. The same approach works whether you are debugging a Jakarta EE application on WildFly, a plain Java SE process, or a message-driven bean inside an EJB container.
| Configuration approach | Best for | Requires extra firewall rules | Works through corporate proxies |
|---|---|---|---|
| Direct JDWP over network | Internal trusted subnets only | Yes | No |
| SSH local forward | Most production-like setups | No | With ProxyCommand |
| VPN plus JDWP | Multi-region distributed teams | No | No |
| kubectl port-forward | Kubernetes-hosted workloads | Cluster rules only | Depends on cluster |
Building the SSH tunnel that carries the debug traffic
A local forward is the most common option: the IDE listens on a local port and forwards the connection to the JDWP socket on the remote host. The simplest form is ssh -L 5005:localhost:5005 user@remote-host. Both port numbers can match any chosen value, as long as the local port matches what NetBeans listens on.
When the target host is not directly reachable, a jump host comes into play. Australian teams frequently bounce through a bastion in Sydney or Melbourne before reaching an application server in a private subnet. The syntax -J jumpuser@bastion is enough for OpenSSH 7.3 and later, and NetBeans uses the same SSH configuration when the IDE itself opens the tunnel.
For users who prefer the IDE to manage the tunnel, NetBeans reads entries from ~/.ssh/config. A Host block with LocalForward 5005 localhost:5005 and the right user plus hostname means the developer simply selects the entry from project properties. The IDE does not display tunnel diagnostics, so a terminal window stays useful for quick checks.
Pointing NetBeans at the remote JVM
Once the tunnel is up, open the project and choose Run → Attach Debugger. Pick SocketAttach as the connector, leave the host as localhost, and enter the forwarded port. Clicking OK should suspend the remote process and drop the editor into debug mode almost instantly.
For developers who want the same configuration between sessions, settings can be stored at the project level. Right-click the project, choose Properties → Debug, and define a custom debug command that runs the SSH forward in the background. NetBeans starts the tunnel when the project is debugged and tears it down when it stops.
The static analysis tools guide goes further into combining debugging sessions with code-quality checks, especially when chasing memory leaks in long-running services.
JVM options the remote side needs
A JVM only accepts debugger connections when started with the right flags. On the remote host, the application must be launched with -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005. The suspend=n part lets the app boot normally and only attach when the IDE connects, which matters for production-adjacent servers.
Container-based deployments need the same flags inside the Dockerfile or orchestration manifest. Kubernetes users in Australian cloud regions, including the local AWS zones in Sydney and Melbourne, often expose JDWP through a sidecar container or kubectl port-forward. The address must remain consistent across restarts, otherwise the IDE loses the connection.
When the application uses JPMS modules, an extra flag is sometimes required. --add-exports and --add-opens let the debugger reach into named modules. Without them, breakpoints resolve but variable inspection returns placeholder values, a frequent source of confusion.
Working with firewalls and high-latency links
Australian geography stretches the distance between developer and server further than most markets. A session from Perth to a Sydney data centre adds around 50 milliseconds of round-trip time, and developers working from Broome or Alice Springs can easily see 100 milliseconds or more. SSH handles the latency well, but the IDE may take a few extra seconds to display variable values.
To keep things responsive, reduce the breakpoint density. Instead of stopping on every loop iteration, set a guarded breakpoint that fires only when a counter reaches a threshold. NetBeans supports breakpoint conditions and hit counts directly in the gutter menu, which makes this practical without restarting the application.
Corporate firewalls in Australia often block outbound SSH to non-standard ports, so sticking to port 22 is safer. If the firewall only allows HTTP and HTTPS, an alternative is to tunnel SSH over an HTTPS proxy using corkscrew or connect-proxy, configured through ProxyCommand in ~/.ssh/config. The NetBeans IDE does not need to know about the proxy when this is done at the SSH layer.
Recovering from dropped sessions and building stable habits
A dropped tunnel is the most common frustration. The remote JVM keeps running, but the IDE shows a disconnected state and breakpoints appear hollow. Rather than restarting the application, simply re-run Run → Attach Debugger with the same parameters. The server-side socket stays open and accepts the new connection, provided server=y was specified.
If reattaching fails, check whether the SSH tunnel is alive. Use ssh -O check user@host for the connection status, or lsof -i :5005 on the local machine to confirm nothing else is squatting on the forwarded port. A stale SSH process often blocks the port, and killing it with ssh -O exit user@host restores the clean state.
Once the session is stable, a few habits prevent the same failure from repeating on the next on-call shift. The list below captures what experienced Australian teams usually standardise.
- Pin JDWP to a fixed port in deployment scripts so the IDE configuration never drifts.
- Use
~/.ssh/configHost blocks to store bastion and jump host details in one place. - Prefer
suspend=non production-like environments to avoid holding the JVM at boot. - Keep the local forward alive with ServerAliveInterval in the SSH client config.
- Disable JDWP entirely on internet-facing hosts and rely on temporary tunnels when needed.
- Save the Attach Debugger settings at the project level so any team member can reproduce the setup.
- Document the tunnel command in the project README so on-call engineers can connect quickly.