When working with SSH connections using JSch (a popular Java SSH library), it’s important to configure timeouts and keep-alive behavior correctly. This helps you:
- Avoid hanging indefinitely when a server or network becomes unresponsive
- Detect dead connections in a predictable way
- Keep long-lived sessions alive across unstable networks
This article explains how to manage timeouts and keep-alive (server-alive) messages in JSch, and clarifies how the different settings interact internally.
1. Timeouts and keep-alive in JSch: the concepts
JSch gives you three main knobs related to “how long to wait” and “how to detect dead connections”:
- Connect timeout
How long JSch waits while establishing the TCP/SSH connection before giving up. -
Socket read timeout
How long JSch waits for data when reading from the socket before treating it as a timeout. -
Server-alive (SSH-level keep-alive) interval and count
Periodic “are you alive?” messages sent by the client when the connection is idle, to detect broken connections.
A key detail in JSch’s implementation is that setTimeout(int) and setServerAliveInterval(int) share the same internal timeout field. That means whichever one you call last will determine the effective socket read timeout.
2. Configuring timeouts with setTimeout and connect(...)
2.1 Session.setTimeout(int timeout)
You configure the session timeout (in milliseconds) using:
session.setTimeout(30_000); // 30 seconds
In JSch, this value is used as:
- The socket read timeout, and
- The default connection timeout when you call:
session.connect(); // no argument
If no data is received within this timeout during a read operation, JSch throws an exception (typically a java.net.SocketTimeoutException wrapped in a JSch exception).
2.2 Session.connect() vs Session.connect(int connectTimeout)
You can control the connect timeout in two ways:
- Implicit connect timeout via
setTimeoutsession.setTimeout(30_000); session.connect(); // uses 30 seconds as connect timeout and read timeout - Explicit connect timeout via overloaded
connect(int)session.setTimeout(30_000); // read timeout session.connect(10_000); // connect timeout = 10 seconds
Here:
10_000ms is used only for the time spent establishing the connection.- The socket read timeout after connection is still
30_000ms (fromsetTimeout, or fromsetServerAliveIntervalif you call that later).
3. Enabling SSH-level keep-alive with setServerAliveInterval
JSch provides a mechanism often referred to as server-alive messages (sometimes called SSH-level keep-alive). These are SSH protocol messages sent by the client when the connection is idle.
- ⚠️ This is not the same as TCP
SO_KEEPALIVE. - JSch’s server-alive feature is implemented at the SSH layer, not as a low-level TCP socket option.
You configure it using:
session.setServerAliveInterval(15_000); // 15 seconds
session.setServerAliveCountMax(3); // send up to 3 unanswered keep-alives
setServerAliveInterval(intervalMillis)
JSch will send a server-alive message if no data is received forintervalMillismilliseconds.-
setServerAliveCountMax(count)
Ifcountconsecutive server-alive messages go unanswered, JSch treats the connection as dead and disconnects.
3.1 Important interaction: setServerAliveInterval and setTimeout
Internally, JSch uses a single timeout field that both setTimeout(int) and setServerAliveInterval(int) influence. That means:
session.setTimeout(30_000); // 30 seconds
session.setServerAliveInterval(15_000);
After these calls, the effective socket read timeout becomes 15 seconds, because setServerAliveInterval(15_000) updates the same internal timeout used for reads.
In other words:
setTimeoutandsetServerAliveIntervalare not independent.- The value set last will be the one that applies to socket read timeouts.
This is a common source of confusion, and it’s important to keep in mind when combining these settings.
4. Example: session with timeout and keep-alive
The following example demonstrates a typical configuration where you:
- Use a single value for read timeout and keep-alive interval (to match JSch’s internal behavior), and
- Use a different value for the connect timeout via
connect(int).
package org.kodejava.jsch;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
public class JSchTimeoutExample {
public static void main(String[] args) {
try {
JSch jsch = new JSch();
Session session = jsch.getSession("username", "host", 22);
// Set credentials
session.setPassword("password");
// Configure session
// In production, avoid disabling StrictHostKeyChecking like this
// and make sure host keys are managed securely.
session.setConfig("StrictHostKeyChecking", "no");
// Configure the session timeout and keep-alive using a single value.
// In JSch, setServerAliveInterval() internally updates the same
// timeout as setTimeout(), so they share the same underlying value.
int timeoutAndKeepAliveMs = 15_000;
// This sets both:
// - the SSH-level keep-alive interval, and
// - the internal timeout used for socket read operations.
session.setServerAliveInterval(timeoutAndKeepAliveMs);
session.setServerAliveCountMax(3);
// Optionally, use a separate (shorter) connect timeout:
// This value only affects how long we wait to establish the connection.
session.connect(10_000); // 10 seconds connect timeout
// Perform your SSH operations here...
// e.g., open channels, execute commands, transfer files, etc.
// Disconnect when done
session.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
This configuration gives you:
- Keep-alive interval: 15 seconds
- Socket read timeout: 15 seconds (same underlying value)
- Connect timeout: 10 seconds (via
connect(10_000))
5. Option: only timeout, without keep-alive
In some environments, you may not need JSch’s server-alive feature:
- The network is stable and connections are short-lived, or
- The server or your application has its own mechanism to detect idle / dead connections.
In that case, you can keep the configuration simpler and only use setTimeout.
5.1 Only timeout, using the same value for connect and read
JSch jsch = new JSch();
Session session = jsch.getSession("username", "host", 22);
session.setPassword("password");
// For production, manage host keys properly instead of disabling this.
session.setConfig("StrictHostKeyChecking", "no");
// 30 seconds socket read timeout.
// This value is also used as the default connect timeout
// when calling connect() without parameters.
session.setTimeout(30_000);
session.connect(); // connect timeout = 30 seconds, read timeout = 30 seconds
// ... use the session ...
session.disconnect();
5.2 Different connect timeout vs read timeout
If you want a shorter connect timeout but a longer read timeout, you can combine setTimeout with connect(int):
JSch jsch = new JSch();
Session session = jsch.getSession("username", "host", 22);
session.setPassword("password");
session.setConfig("StrictHostKeyChecking", "no");
// Read timeout after connection is established
session.setTimeout(30_000); // 30 seconds
// Connect timeout for establishing the TCP/SSH connection
session.connect(10_000); // 10 seconds
// ... use the session ...
session.disconnect();
Here:
- If the server cannot be reached within 10 seconds,
connect(10_000)fails. - Once connected, read operations will time out after 30 seconds of inactivity.
6. Option: custom keep-alive logic
Instead of relying on JSch’s server-alive feature, you can implement a custom keep-alive in your application logic. This gives you more control and visibility.
A common pattern:
- Use a scheduler (e.g.
ScheduledExecutorService) to run a task every N seconds. - That task sends a lightweight command to the server via an SSH channel (for example
echo 1ortrue). - If the command fails, times out, or throws an exception, treat the session as broken and:
- Close the session, and
- Optionally create a new one.
This approach is more verbose but can be useful when you need:
- Application-level monitoring of connection health
- Detailed logging of keep-alive failures
- Integration with your own reconnection or failover logic
7. Best practices and gotchas
To wrap up, here are some practical recommendations:
- Be aware of the shared timeout field
Remember thatsetTimeout(int)andsetServerAliveInterval(int)share the same internal timeout. The last one called effectively “wins” for the socket read timeout. -
Use
connect(int)for fine-grained control of connect timeout
If you care about “fast fail” when a server is unreachable, always useconnect(int connectTimeout)instead of plainconnect(). -
Don’t disable
StrictHostKeyCheckingin production
The example uses:session.setConfig("StrictHostKeyChecking", "no");This is convenient for demos and testing, but insecure in production. Properly manage known hosts and host key verification.
-
Tune keep-alive carefully on unstable networks
A very short server-alive interval can cause aggressive disconnects on noisy networks. Start with moderate values (e.g., 15–30 seconds interval, 3–5 max count) and adjust based on real-world behavior. -
Always close sessions cleanly
Callsession.disconnect()when you’re done. Leaking sessions can exhaust resources on both client and server.
By understanding how JSch handles timeout and keep-alive settings internally—especially the shared timeout field used by setTimeout and setServerAliveInterval—you can configure your SSH sessions to behave predictably and handle network issues more gracefully.
