- DigitalOcean
- Linux
- Java
- Gradle
- Setup and configure a server on DigitalOcean
- Create and configure a new Linux user on the Droplet (security best practice)
- Deploy and run a Java Gradle application on Droplet
In DigitalOcean, a Droplet is a Virtual Private Server (VPS). It acts as a lightweight, scalable virtual machine (VM) in the cloud, providing dedicated resources like CPU, RAM, and SSD storage, along with a unique public IP address.
By creating a Droplet, you gain full control over the Operating System (e.g., Ubuntu, CentOS), allowing you to manage it like a standalone physical server.
Note
In this example, we assume your Droplet's public IP is 456.456.456.456. You will receive your actual IP address via the DigitalOcean UI after creation.
The local machine used in this example is a laptop running Ubuntu 22.04.5.
For this setup, we're using Ubuntu 22.04 and a "Basic" plan.
- Choose an image: Select Ubuntu 22.04 (LTS).
- Choose a plan: Select the "Basic" Shared CPU plan.
- CPU options: Select regular disk type and basic resources.
- Authentication method: Select SSH Key.
To securely access your server, click New SSH Key and follow the on-screen instructions. If you already have a key on your local machine, you can copy it using this command:
# Print your public SSH key to the terminal
cat ~/.ssh/id_rsa.pubSecurity is a priority. Before doing anything else, we will restrict access to the server. Navigate to Networking > Firewalls in the DigitalOcean dashboard and click Create Firewall.
Define which traffic is allowed to reach your server. For now, we will only allow SSH access.
| Type | Protocol | Port Range | Sources | Purpose |
|---|---|---|---|---|
| SSH | TCP | 22 | <your-local-machine-public-IP> |
Allows only YOU to access the server terminal. |
- Find your IP: You can find your current local IP at whatismyip.com.
- Restricting SSH: This rule ensures that only devices on your specific network can attempt to log in.
Note: We will open the application port in a later step.
Apply the firewall to your Droplet and save the changes.
For security best practices, you should not perform daily tasks as the root user. Instead, create a dedicated admin user using the provided create_admin_user.sh script.
What this script does:
- User Creation: Creates a new system user with a dedicated home directory and Bash access.
- Elevated Privileges: Adds the user to the
sudogroup, allowing them to run administrative commands. - SSH Security: Automatically creates the
.sshdirectory and injects your public ssh-key so you can log in without a password. - Permission Hardening: Strictly limits access to the SSH folder (
700) and keys (600) so other users on the system cannot read them.
Execution:
- Transfer the setup script: From your local machine, copy the
create_admin_user.shscript to the remote server usingscp(Secure Copy).
# Usage: scp <local-path> root@<ip-address>:<remote-path>
scp ./scripts/create_admin_user.sh root@456.456.456.456:/root- Access the Droplet via ssh:
ssh root@456.456.456.456- Execute the script on the server: Once logged in, give the script execution permissions and run it. You must provide your desired username and your public SSH Key (the same string you used in Step 1) as arguments.
# Grant execution permissions
chmod u+x create_admin_user.sh
# Run the script: ./create_admin_user.sh "username" "public-ssh-key-string"
./create_admin_user.sh "newadmin" "ssh-rsa AAAAB3NzaC..."- Exit the server: Once the user is created, log out.
exitTo build this application, you can use OpenJDK 17 and Gradle 9.2.1 on your local machine.
- Build the JAR: Run the following command from the project root on your local machine:
gradle buildThis generates the artifact at ./build/libs/java-react-example.jar.
- Install Java on the server: Login to your Droplet using your new admin user.
ssh newadmin@456.456.456.456Then, install the Java 17 Runtime Environment (JRE):
sudo apt update
sudo apt install openjdk-17-jre-headlessYou can verify that Java is available running the java -version command.
- Transfer files: Open a new terminal on your local machine and copy the JAR and the setup script to your admin user's home directory.
scp ./build/libs/java-react-example.jar ./scripts/setup_app_env.sh newadmin@456.456.456.456:/home/newadmin/We will now create a dedicated system user to run the application. This follows the Principle of Least Privilege, ensuring that if the application is compromised, the attacker has no access to the rest of the system.
What the setup_app_env.sh script does:
- Isolates the App: Creates a system user with no login shell and no home directory.
- Sets Up Directories: Creates
/opt/<app_name>for the binary and a/logssub-directory. - Hardens Permissions: Ownership of the application directory is given to
rootso the service user cannot modify the executable, while the/logsfolder is made writable for the app.
Execution
- Run the environment script: Log back into your server (as
newadmin) and run the script.
sudo chmod u+x setup_app_env.sh
# Usage: ./setup_app_env.sh <app_name> <service_user_name> <app_port>
sudo ./setup_app_env.sh "my-java-app" "appjavauser" 7071- App Name: Determines the folder name in
/opt/. - Service User: The restricted user that will run the Java process.
- Port: The port your application is configured to listen on.
Tip: If you are unsure which port your app uses, check your
application.ymlsource code or runnetstat -lpnton your local machine while the app is running to see the Local Address column.
- Move the JAR: Transfer the file from your home directory to the protected application folder created by the script.
sudo mv /home/newadmin/java-react-example.jar /opt/my-java-app/- Lock down permissions: We set the owner to
rootso the service user (which runs the app) cannot modify or delete its own binary. This prevents a compromised app from "self-modifying".
# Root owns the file; the app group can read/execute it
sudo chown root:appjavauser /opt/my-java-app/java-react-example.jar
# chmod 750: Owner=rwx (7), Group=r-x (5), Others=--- (0)
# This allows the owner to read/write/execute, the group to read/execute, and others have no access
sudo chmod 750 /opt/my-java-app/java-react-example.jarNow that the application environment is set up and you have confirmed the port (used in Step 5), you must allow external traffic to reach it.
- Return to the DigitalOcean Dashboard > Networking > Firewalls.
- Edit your existing firewall and add a new Inbound Rule:
| Type | Protocol | Port Range | Sources | Purpose |
|---|---|---|---|---|
| Custom | TCP | 7071 | All IPv4 All IPv6 |
Allows the public to access your app. |
- Port Range: Must match the port you specified in Step 5 (e.g.,
7071). - Sources: Leaving this as "All" allows anyone on the internet to view your web app.
To ensure the application starts automatically on boot and restarts if it crashes, we use a Systemd service.
- Create the service file: Use a text editor like
vimornanoto create the configuration file.
sudo vim /etc/systemd/system/my-java-app.service- Add the configuration: Copy the contents of the provided
scripts/service.txtinto this file and save it.
NOTE: If you chose a different
app_nameorservice_userin Step 5, you must update the paths and user fields in this file to match your specific setup.
User=appjavauser-> Change to your service user.ExecStart=...-> Update the path to your JAR file.WorkingDirectory=...-> Update the path to/opt/YOUR_APP_NAME.
Run these commands to register and launch your service:
# Reload systemd daemon to detect the new .service file
sudo systemctl daemon-reload
# Enable the service to start automatically on system boot
sudo systemctl enable my-java-app
# Start the application service immediately
sudo systemctl start my-java-appYou can now check the status of your service:
sudo systemctl status my-java-appIf it is active (running), verify access by opening your browser and navigating to http://456.456.456.456:7071.
This project is a part of a presentation
Related projects:
- react-intro - Introduction to react and redux.
- java-webpack-example - Advanced example showing how to use a module bundler in a Java project.
Tip: How to enable LiveReload in IntelliJ
Original project can be found here: java-react-example



