How to Guarantee a Single Instance of Spring Boot 3 on One Server

This article explains why running only one Spring Boot instance per server is essential for stability and data integrity, and presents four practical techniques—file locks, FileChannel locks, port binding, and IPC sockets—complete with code samples to prevent duplicate startups.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
How to Guarantee a Single Instance of Spring Boot 3 on One Server

In production environments, ensuring that a Spring Boot application runs as a single instance on a server is crucial for system stability, resource efficiency, and data consistency.

Multiple instances can cause port conflicts, increased CPU and memory usage, duplicate scheduled tasks, and cache inconsistencies, leading to service crashes and data errors.

Four methods to enforce a single instance

1. File lock (simple)

The application creates a temporary lock file at startup; if the file already exists, the program exits, indicating another instance is running.

@SpringBootApplication
public class App {
    private static final String LOCK_FILE = "app.lock";
    public static void main(String[] args) {
        File lockFile = new File(LOCK_FILE);
        try {
            // 1. Check if file exists
            if (lockFile.exists()) {
                System.out.println("Application is already running, do not start again...");
                System.exit(1);
            }
            // 2. Create file
            if (!lockFile.createNewFile()) {
                throw new RuntimeException("Failed to create lock file");
            }
            System.err.printf("Lock file located: %s%n", lockFile.getAbsolutePath());
            // 3. Add shutdown hook to delete lock file
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                if (lockFile.delete()) {
                    System.out.println("Lock file deleted.");
                }
            }));
        } catch (Exception e) {
            System.err.printf("Startup failed: %s%n", e.getMessage());
            System.exit(1);
        }
        SpringApplication.run(App.class, args);
    }
}

When the application is started a second time, it prints:

Application is already running, do not start again...

Note: the application must be stopped gracefully; otherwise the lock file remains and prevents future starts.

2. File lock (using FileChannel and FileLock)

This approach acquires an OS‑level file lock, which avoids the race condition of merely checking file existence.

try {
    // 1. Create lock file
    Path lockPath = new File("app.lock").toPath();
    FileChannel channel = FileChannel.open(lockPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
    // 2. Try to acquire lock
    FileLock lock = channel.tryLock();
    if (lock == null) {
        System.err.println("Application is already running, do not start again...");
        System.exit(1);
    }
    // 3. Add shutdown hook to release lock and delete file
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
        try {
            if (lock != null) {
                lock.release();
            }
            if (channel != null) {
                channel.close();
            }
            new File("app.lock").delete();
        } catch (Exception ignored) {}
    }));
    // 4. Normal startup
    SpringApplication.run(App.class, args);
} catch (IOException e) {
    System.err.printf("Startup failed: %s%n", e.getMessage());
    System.exit(1);
}

3. Network port binding

Attempt to bind a specific port; if binding fails, another instance is already using it.

try (ServerSocket socket = new ServerSocket(PORT)) {
    System.out.println("Service can start normally...");
} catch (IOException e) {
    System.err.println("Application is already running, do not start again...");
    System.exit(1);
}
SpringApplication.run(SpringBootSingleInstanceApplication.class, args);

This method is simple but cannot distinguish between a Spring Boot instance and another unrelated service occupying the same port.

4. Inter‑process communication (IPC) socket

Bind a loopback socket; if the bind succeeds, no other instance is running. The socket can also be used to query the service status.

try {
    ServerSocket ipcSocket = new ServerSocket(IPC_PORT, 1, InetAddress.getLoopbackAddress());
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
        try { ipcSocket.close(); } catch (Exception ignored) {}
    }));
    // IPC listener thread
    new Thread(() -> {
        while (true) {
            try (Socket client = ipcSocket.accept()) {
                client.getOutputStream().write("ALIVE".getBytes(StandardCharsets.UTF_8));
            } catch (Exception ignored) {}
        }
    }).start();
    SpringApplication.run(SpringBootSingleInstanceApplication.class, args);
} catch (Exception e) {
    System.err.println("IPC channel is already in use");
    System.exit(1);
}

Using IPC avoids false positives caused by other services occupying the same port and allows precise process‑level control.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaSpring BootIPCFile LockNetwork PortSingle Instance
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.