Deploying a Java Application on VPS: Advanced Level with CI/CD, Docker and SSL
In this article, we will look at the advanced process of deploying a Java application on a VPS (Virtual Private Server). We will not dwell on basic steps like installing JDK or running a JAR file. Instead, we will focus on setting up a CI/CD pipeline, containerization with Docker, using Nginx as a reverse proxy, automatically obtaining SSL certificates, as well as monitoring and fault tolerance of the application.
It is assumed that you already have a VPS with Ubuntu 22.04 LTS, a base domain, and are familiar with the Linux command line. All code examples are written in Java 17+ using Spring Boot, but the principles apply to any Java framework.
1. Containerizing a Java Application with Docker
The first step towards advanced deployment is packaging the application into a Docker container. This ensures environment reproducibility and simplifies deployment.
Create a Dockerfile in the project root:
# Use multi-stage build to reduce the size of the final imageFROM maven:3.9.6-eclipse-temurin-17 AS builderWORKDIR /appCOPY pom.xml .RUN mvn dependency:go-offlineCOPY src ./srcRUN mvn clean package -DskipTests
FROM eclipse-temurin:17-jre-alpineWORKDIR /appCOPY --from=builder /app/target/*.jar app.jarEXPOSE 8080ENTRYPOINT ["java", "-jar", "app.jar"]docker-compose.yml file for running with a database:
version: '3.8'services: app: build: . ports: - "8080:8080" environment: - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/mydb - SPRING_DATASOURCE_USERNAME=user - SPRING_DATASOURCE_PASSWORD=pass depends_on: - db restart: unless-stopped
db: image: postgres:15-alpine environment: POSTGRES_DB: mydb POSTGRES_USER: user POSTGRES_PASSWORD: pass volumes: - pgdata:/var/lib/postgresql/data restart: unless-stopped
volumes: pgdata:After building the image with the command docker compose build, you will have a container ready for deployment.
2. Setting up a CI/CD Pipeline (GitHub Actions)
Deployment automation is a key element of the advanced level. We use GitHub Actions to build, test, and send the image to the server.
Create the file .github/workflows/deploy.yml:
name: Deploy Java App to VPS
on: push: branches: [ main ]
jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin'
- name: Build with Maven run: mvn clean package
- name: Build Docker image run: docker build -t myapp:latest .
- name: Save Docker image to tar run: docker save myapp:latest | gzip > myapp.tar.gz
- name: Copy image to VPS via SCP uses: appleboy/scp-action@v0.1.7 with: host: ${{ secrets.VPS_HOST }} username: ${{ secrets.VPS_USER }} key: ${{ secrets.VPS_SSH_KEY }} source: "myapp.tar.gz" target: "/opt/app/"
- name: Deploy on VPS uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.VPS_HOST }} username: ${{ secrets.VPS_USER }} key: ${{ secrets.VPS_SSH_KEY }} script: | cd /opt/app docker load < myapp.tar.gz docker compose down docker compose up -d rm myapp.tar.gzDon't forget to add the secrets VPS_HOST, VPS_USER, VPS_SSH_KEY in the settings