--- title: "Cómo desplegar Spring Boot en Ubuntu 24.04 con Nginx" description: "Despliega Spring Boot en Ubuntu 24.04 con Java 21: compila el JAR, ejecútalo como servicio de systemd y publícalo tras Nginx con HTTPS de Let's Encrypt." category: "E-commerce y CMS" language: es source: https://cubepath.com/es/docs/e-commerce-y-cms/implementacion-de-aplicacion-spring-boot-en-linux updated: 2026-09-25 --- # Cómo desplegar Spring Boot en Ubuntu 24.04 con Nginx Spring Boot empaqueta la aplicación y su servidor web (Tomcat por defecto) en un único JAR ejecutable, así que desplegarla consiste en instalar Java, copiar el JAR y mantenerlo en marcha. En este tutorial compilarás una aplicación Spring Boot en Ubuntu 24.04 con Java 21, la ejecutarás como servicio de systemd con un usuario sin privilegios y la publicarás detrás de Nginx con un certificado de Let's Encrypt. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 2 GB de RAM. - Un usuario no root con privilegios `sudo`. - Un dominio con un registro DNS `A` apuntando a la IP del servidor. En los ejemplos se usa `your_domain`. - Una aplicación Spring Boot 3 o 4 con Maven o Gradle, o las ganas de probar con un proyecto nuevo generado en Spring Initializr. La aplicación de ejemplo se llama `myapp`. Sustituye el nombre en todos los comandos y archivos. ## Paso 1: Instalar Java 21 Spring Boot 3 y 4 requieren Java 17 o superior. Ubuntu 24.04 incluye OpenJDK 21, la versión LTS recomendada. Instala el JDK, que incluye el compilador necesario para construir el JAR en el servidor, junto con Nginx y `unzip`: ```bash sudo apt update sudo apt install openjdk-21-jdk-headless nginx unzip ``` Comprueba la versión: ```bash java -version ``` ```output openjdk version "21.0.x" ... OpenJDK Runtime Environment (build 21.0.x+...-Ubuntu-...) OpenJDK 64-Bit Server VM (build 21.0.x+..., mixed mode, sharing) ``` > Nota: si compilas el JAR en tu equipo o en un pipeline de CI y solo lo copias al servidor, basta con el paquete `openjdk-21-jre-headless`. Abre en el cortafuegos SSH, HTTP y HTTPS. El puerto 8080 de la aplicación no se abre: solo Nginx accederá a él de forma local. ```bash sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable ``` ## Paso 2: Compilar la aplicación Si tienes la aplicación en un repositorio, clónala y compílala con el wrapper que incluye el proyecto (`mvnw` o `gradlew`), que descarga la versión correcta de Maven o Gradle: ```bash git clone https://github.com/your_account/myapp.git ~/myapp cd ~/myapp ./mvnw -DskipTests package ``` Con Gradle, el equivalente es `./gradlew bootJar` y el JAR queda en `build/libs/`. Si solo quieres probar el despliegue, genera un proyecto con Spring Initializr que incluya Spring Web y Actuator, que aporta el endpoint de salud `/actuator/health`: ```bash curl -fsSL https://start.spring.io/starter.zip -d type=maven-project -d javaVersion=21 -d dependencies=web,actuator -d artifactId=myapp -d name=myapp -o myapp.zip unzip myapp.zip -d ~/myapp cd ~/myapp ./mvnw -DskipTests package ``` La primera compilación descarga las dependencias y tarda un par de minutos. Al terminar, comprueba que existe el JAR: ```bash ls target/*.jar ``` ```output target/myapp-0.0.1-SNAPSHOT.jar ``` ## Paso 3: Crear el usuario y el directorio de la aplicación Ejecutar la aplicación con un usuario de sistema sin shell limita el daño si alguien la compromete. Crea el usuario `myapp` y el directorio `/opt/myapp`: ```bash sudo useradd --system --home-dir /opt/myapp --shell /usr/sbin/nologin myapp sudo mkdir -p /opt/myapp/config ``` Copia el JAR con un nombre fijo, para que la unidad de systemd no tenga que cambiar con cada versión: ```bash sudo cp ~/myapp/target/myapp-0.0.1-SNAPSHOT.jar /opt/myapp/myapp.jar ``` ## Paso 4: Configurar la aplicación para producción Spring Boot carga automáticamente `config/application.properties` desde el directorio de trabajo, y sus valores tienen prioridad sobre los empaquetados en el JAR. Así puedes mantener la configuración de producción fuera del código: ```bash sudo nano /opt/myapp/config/application.properties ``` ```properties server.address=127.0.0.1 server.port=8080 server.forward-headers-strategy=native management.endpoints.web.exposure.include=health ``` - `server.address=127.0.0.1` hace que Tomcat solo acepte conexiones locales. - `server.forward-headers-strategy=native` hace que la aplicación use las cabeceras `X-Forwarded-*` de Nginx para conocer la IP del cliente y el esquema `https`. - `management.endpoints.web.exposure.include=health` expone solo el endpoint de salud de Actuator. Si la aplicación usa una base de datos, añade aquí también `spring.datasource.url`, `spring.datasource.username` y `spring.datasource.password`. Asigna los archivos al usuario de la aplicación y protege la configuración, que puede contener contraseñas: ```bash sudo chown -R myapp:myapp /opt/myapp sudo chmod 640 /opt/myapp/config/application.properties ``` ## Paso 5: Crear el servicio de systemd Crea la unidad del servicio: ```bash sudo nano /etc/systemd/system/myapp.service ``` ```ini [Unit] Description=myapp Spring Boot application After=network-online.target Wants=network-online.target [Service] User=myapp Group=myapp WorkingDirectory=/opt/myapp ExecStart=/usr/bin/java -Xms256m -Xmx512m -jar /opt/myapp/myapp.jar SuccessExitStatus=143 Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target ``` - `-Xms256m -Xmx512m` fijan el tamaño mínimo y máximo del heap. Ajusta `-Xmx` a la memoria del servidor, dejando margen para el sistema y para la memoria de la JVM que no es heap. - `SuccessExitStatus=143` indica a systemd que la salida de Java al recibir `SIGTERM` es una parada normal y no un fallo. Activa e inicia el servicio: ```bash sudo systemctl daemon-reload sudo systemctl enable --now myapp ``` El arranque tarda unos segundos. Sigue el log hasta ver que Tomcat escucha: ```bash sudo journalctl -u myapp -f ``` ```output ... TomcatWebServer : Tomcat started on port 8080 (http) with context path '/' ... MyappApplication : Started MyappApplication in 2.9 seconds (process running for 3.4) ``` Pulsa `Ctrl+C` para salir y comprueba el endpoint de salud: ```bash curl http://127.0.0.1:8080/actuator/health ``` ```output {"status":"UP"} ``` ## Paso 6: Configurar Nginx como proxy inverso Crea un bloque de servidor para el dominio: ```bash sudo nano /etc/nginx/sites-available/myapp ``` El archivo `proxy_params` de Ubuntu añade las cabeceras `Host`, `X-Real-IP`, `X-Forwarded-For` y `X-Forwarded-Proto` que la aplicación espera: ```nginx server { listen 80; listen [::]:80; server_name your_domain www.your_domain; client_max_body_size 20M; location / { include proxy_params; proxy_pass http://127.0.0.1:8080; } } ``` Activa el sitio, desactiva el sitio por defecto y comprueba la sintaxis: ```bash sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/ sudo rm /etc/nginx/sites-enabled/default sudo nginx -t sudo systemctl reload nginx ``` Comprueba que Nginx llega a la aplicación: ```bash curl http://your_domain/actuator/health ``` ```output {"status":"UP"} ``` ## Paso 7: Activar HTTPS con Let's Encrypt Instala Certbot con su plugin para Nginx y solicita el certificado. Certbot añade la configuración TLS y la redirección de HTTP a HTTPS: ```bash sudo apt install certbot python3-certbot-nginx sudo certbot --nginx -d your_domain -d www.your_domain ``` Comprueba que la renovación automática funciona: ```bash sudo certbot renew --dry-run ``` Abre `https://your_domain/actuator/health` en el navegador y verás la respuesta `{"status":"UP"}` servida por HTTPS. ## Actualizar la aplicación Para desplegar una nueva versión, compílala, sustituye el JAR y reinicia el servicio: ```bash cd ~/myapp git pull ./mvnw -DskipTests package sudo install -o myapp -g myapp -m 644 target/myapp-0.0.1-SNAPSHOT.jar /opt/myapp/myapp.jar sudo systemctl restart myapp ``` Desde Spring Boot 3.4 el apagado ordenado (graceful shutdown) está activo por defecto, así que las peticiones en curso terminan antes de que el proceso se detenga. ## Solución de problemas - **502 Bad Gateway justo después de reiniciar**: la aplicación aún está arrancando. Espera a ver `Started ...` en `journalctl -u myapp`. - **El servicio se reinicia en bucle**: revisa `sudo journalctl -u myapp -n 100`. Las causas típicas son un error en `application.properties`, una base de datos inaccesible o `java.lang.OutOfMemoryError`, que se corrige subiendo `-Xmx` si el servidor tiene memoria libre. - **`Web server failed to start. Port 8080 was already in use`**: otro proceso usa el puerto. Localízalo con `sudo ss -ltnp | grep 8080` o cambia `server.port` y el `proxy_pass` de Nginx. - **Las redirecciones de la aplicación apuntan a `http://`**: falta `server.forward-headers-strategy=native` o Nginx no está enviando `X-Forwarded-Proto`. ## Conclusión Tu aplicación Spring Boot se ejecuta como servicio de systemd con un usuario sin privilegios, escucha solo en local y Nginx la publica por HTTPS. Como siguientes pasos puedes: - Añadir una base de datos PostgreSQL o MySQL y configurar `spring.datasource.*` en el archivo de producción. - Exponer métricas de Actuator para Prometheus y proteger esos endpoints. - Construir el JAR en un pipeline de CI y copiar solo el artefacto al servidor.