Un poco del contexto de este sitio lo encuentras en este link Quien soy yo
Parte 5 LAMP Carrito de compras sin REACT con Spring Data JPA
Hemos hablado antes de que muchas personas dejan mal configurados los servidores. Yo no pretendo decirte como usar springboot o hibernate paso a paso. No conozco tu entorno. Pero si se que REACT está causando muchos problemas. El principal es que con conexion decente y pc decente en proyectos medio complicados cada build compile bien hecho puede tardar 8 minutos. Por ejemplo, para corregir una falta de ortografía o un cambio de color. Mucho del problema son los pasos intermedios que tiene REACT. Y que del 2022 al 2025 explotó en México esta tecnología del 2014, si.
En 2026, ¿por qué un «Hola Mundo» en React necesita descargar 400 MB de node_modules?
Sumale que Maven recompila todo cada vez y Gradle solo lo que cambió.
Vamos a revisar y actualizar un poco un código de terceros, que es de 2019, y que usa Thymeleaf y JPA Spring no hibernate en 2019. No es perfecto pero es una prueba de concepto.
Pero el primer punto es limitar la memoria. No lo hagas si no entiendes como configurar hibernate. Puedes seguir este «tutorial» sin hacer esto, pero este es el bonus experto:
La Precaución Crítica: Hibernate NO Puede Comerse Todo
El código que usaremos de ejemplo es de 2019. En ese entonces y ahora muchos asumen que Hibernate podía usar toda la RAM disponible era «aceptable» porque los servidores tenían 8-16 GB. En 2026, con empresas medianas, eso es suicidio por el costo de la nube sea cual sea tu memoria..
Configuración application.properties REAL para mundo real:
# === LÍMITES DE POOL DE CONEXIONES ===
spring.datasource.hikari.maximum-pool-size=5
spring.datasource.hikari.minimum-idle=2# === HIBERNATE: NO CACHEES TODO ===
spring.jpa.properties.hibernate.cache.use_second_level_cache=false
spring.jpa.properties.hibernate.cache.use_query_cache=false# === LÍMITE DE STATEMENTS PREPARADOS ===
spring.datasource.hikari.max-lifetime=600000# === LOGGING (para diagnosticar) ===
logging.level.org.hibernate.SQL=WARN
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=WARN¿ Por Qué Estas Líneas Salvan Tu Servidor ?
Sin configurar:
– Pool conexiones: 10 (default) × 30 MB = 300 MB (y a veces tienes 20 o mas)
– Cache L2 Hibernate: Crece hasta 500-800 MB
– Statements preparados: Sin límite = leak de memoria
– Total desperdiciado: ~1.2 GB**Con configuración:
– Pool conexiones: 5 × 30 MB = 150 MB
– Cache L2: Deshabilitado = 0 MB
– Statements: Con lifetime = No leak
– Total usado: ~200 MB
El Código que vamos a ver es de 2019 y luego lo vamos a analizar un poco.
NO LO ESCRIBI YO.
Nota muy interesante segun yo:
Quiero dejar claro que este tema me parece curioso especialmente porque en 2017 me encontraba en la situación de tener que contratar programadores php y les daba 3 horas para hacer desde cero, sin objetos, crud de tres tablas inventario, productos y ventas, con la salvedad que consideran que el producto podia tener dos proveedores. Solo el 2- 5 % lo podian hacer. el 70% de los que llegaban no pasaban a examen porque NO me podían hacer un hola mundo en php en la entrevista. Y cuando programadores existentes en una empresa me decían que no se podía hacer en ese tiempo, yo lo hacía desde cero, frente a ellos, sin internet en poco menos de hora y media. Y de pilón evaluaba su conocimiento de base de datos.
De este ejemplo EL concepto principal: Hay Alternativas a React probadas, mas rápidas y eficientes en memoria desde 2019
Ventajas: Muestra Thymeleaf y un crud funcional sin react. Con Thymeleaf Usa el Thymeleaf Layout Dialect, que puedes hacer algo real y reutilizable sin los problemas de REACT. Muestra Gradle, Bootstrap 4 y stack completo de manera mas eficiente que lo que muchos tienen en 2025. Es fácil de entender para los que vienen de otros frameworks porque no usa dependencias raras. Lo puedes reproducir sin descargar medio internet y actualizarlo no es tna difícil.
Problemas: No hay spring security, se reinicia el servidor y se pierde, no te dice nada de hibernate, javax ya fue sustituido por jakarta. Las validaciones solo son una buena idea y deberían ser en backend, es ventaja y deventaja. Podemos decir que es mala UX pero funcional, aunque te salva de ataques de inyección SQL y es simple. El evento viene como es, si se queda a la mitad puede haber venta guardada pero inventario no descontado.
- el proceso se queda a la mitad, se guarda la venta pero no se descuenta el inventario. Eso en 2019 se arreglaba con un simple @Transactional. A final de cuentas no importa si el código original lo tiene o si es parte de las ‘minas’ que tenemos que desactivar al revisar código ajeno.
Resumen: Es simple, funciona, y si bien tiene que adaptarse para crecer, debes dar gracias si ves código como ese en lugar de 1600 líneas para validar existencias.
QUE HARÍA YO: Si me mostraban esto en 2019, o ahora, agarraba la base de datos, pasaba los campos de float a DECIMAL 14,6 o bigdecimal en términos java, y lo reescribía de cero en php y mostraba todo el sistema terminado al día siguiente en un solo archivo con niveles de acceso y respetando el histórico. Y lo hago todavía. En 2025 hice algo similar tres veces y fue un año flojo.
Que tiene este codigo de Parzebyte:
Es código real con problemas reales que resuelve problemas reales mas elegante que mucho que vas a ver. No te estoy diciendo que lo uses en producción, sino que gradle en lugar de maven y thymeleaf te pueden ayudar a reducir el gasto de nube y de pilón veremos como puede arreglarse código heredado sin reescribir todo.
Escribir esto me tomó unos 20 minutos. El análisis de abajo otra media hora.
En el tema anterior puse un carrito de compras que uso como ejemplo. Es muy buen front end pero muy mal back end.
Codigo fuente:
https://parzibyte.me/blog/posts/sistema-ventas-spring-mvc-mysql-bootstrap/
Base de datos:
La base de datos https://github.com/parzibyte/sistema-ventas-spring-boot/blob/master/src/main/resources/esquema_ventas_springboot.sql
Archivo Gradle https://github.com/parzibyte/sistema-ventas-spring-boot/blob/master/build.gradle
El código es limpio y por lo mismo «bien documentado» para entender gradle. Pero…. Hay demasiadas cosas que van a explotar. De entrada no te dice que version de gradle ni requisitos de compilación ni limitaciones a tomar en cuenta. No me extrañaria que el pom sea de otro lugar.
Vuelvo a decir : No lo escribí yo.
Este código es un EXCELENTE tutorial de Spring Boot. Cumple su propósito educativo.
PERO si lo llevas a producción tal cual: Perderás dinero (Float) Tendrás inconsistencias (MyISAM) Crashearás (sin WHERE) y Te multarán (sin @Transactional).
El método de separación de clases se usa para que varios programadores puedan trabajar el mismo tiempo en diferentes partes del proyecto sin pisarse los pies. Pero, siendo sinceros, en 33 años de programación he manejado sistemas con 90 a 100 tablas o con información delicada , y los cambios son relativamente frecuentes y no triviales. Asií que srping boot en un sistema de 50 tablas tienes un problema serio.
La idea de usar hace años laravel y ahora react y springboot (que son de hace diez años ahora en 2026) es ser productivo de inmediato. El código que mencione está hecho sobre spring data JPA que lleva un ORM integrado. Haciendo una revisión en este momento pienso lo msimo que habría pensado en 2019. La persona tomó un curso pero no ha trabajado en algo real. Se agradece la intención eso sí y sirve como ejemplo de FRONT END.
El primer punto que se destaca para mi es que la base de datos usa campos float debiendo ser campos DECIMAL. Es un tema no menor, pero en dinero o vidas humanas cuenta. Usar double, money, currency, float son igualmente malos.
El segundo es que es MYISAM, no INNODB, lo cual es en muchos aspectos mala práctica desde 2013 sea el lenguaje que sea por varias razones.
Un ejemplo simple que puedes buscar el significado en internet.
La Solución de Oro (Que ya existía en 2019): Para arreglar el «Back-end» en cuanto a conexiones inestables, solo falta una línea arriba del método terminarVenta en el VenderController.java:
Java
@Transactional(rollbackFor = Exception.class)
public String terminarVenta(HttpServletRequest request, ...) {
// Si algo falla aquí adentro, Spring hace ROLLBACK automático
// y la base de datos queda como si nada hubiera pasado.
}
-
Nota Senior: Si el autor hubiera usado esta anotación junto con InnoDB, el sistema sería 95% confiable. Pero como usó MyISAM, ¡la anotación no sirve de nada! MyISAM no sabe qué es un Rollback. Es el «doble error» de este proyecto. Pero Inno existe desde varios añes antes de 2019 que se hizo el tutorial.
EL 95% de los problemas estarían resueltos si :
- usando campos decimal en lugar de float y en lasvariables y métodos respectivos.
- innodb en lugar de myisam
- uso de Transactional
Otros puntos intersantes:
- No vi usar where en ningun lado. OutOfMemory con muchos datos o muchas aplicaciones en el servidor o si el usuario esta viendo videos en facebook al mismo tiempo
- En mi punto de vista editar el nombre de producto es un gran NONO jamas y deberia comentarse en cualquier carrito de compras . No puedes sobreescribir codigo de barras, clave hacienda, Nombre o descripción del producto. Te lo digo por experiencia . Hay casos extremos como que reusen el Código para otro producto y es un mega desastre. No es algo menor el problema de recliclar claves de producto. Literalmente Cuesta vidas. Siempre es mejor crear una clave de producto nueva.
- Por JPA y el ORM la base de datos es … nominal. No hay numero de versión. que pasa cuando permites o pones un campo nuevo como tipo de impuesto con el ORM ? y si cambias lo campos de float a decimal ? Deberías checar que versión de base de datos es y si el campo existe.
- request.getSession().setAttribute(«carrito», carrito); // el carrito no expira nunca
A propósito. Estoy de acuerdo en usar el id del producto en el get, pero lo hago por bitácora, no por las mismas razones y borrar lo permito solo a usuarios identificados, y si no hay historial de ventas del producto.
Revisando el navegador del repositorio https://github.com/parzibyte/sistema-ventas-spring-boot
Estas son las notas que tomé..
producto.java
private Float existencia;
// Así se vería variable para mapear el DECIMAL(14,6)
private BigDecimal precio;
// Al recuperarlo de la base de datos o crearlo:
BigDecimal valor = new BigDecimal(«12345678.123456»);
En lugar de bigdecimal o decimal Hay métodos adicionales de multiplicacion que a veces se usan pero son raro verlos. Aquí afortunadamente no estan. que pueden llevar a overflow entre 32 y 64 bits de compilador.
Precisión inmutable: Nunca perderás un micro-centavo en una suma de 10,000 facturas.
/ProductosController.java
Problema: recibe id del formulario, asi que con un f12 o o postmanpuede borrar cualquier producto del sistema. No hay permiso para verifiar si el usuario puede hacerlo o si hay historial asociado de pedidos, existencia compras oventas.
@valid solo verifica que sea mayor a 0. Pero si hay concurrencia dos usuarios sobreescriben el registro. La existencia se leyó hace cinco minutos que cargaron el formulario.
(Como dije antes en lo personal yo considero que es correcto tener el id en el get pero por bitacora, No por las razones que menciona el.)
producto vendido .java.
Poner aqui una lista de precio seria horrible. y como consultas el histórico ? otro problema.
public Float getTotal() {
el modelo en gettotal no considera que pasa si la existencia es cero. y si el precio es negativo ? no deberia ser pero en ocasiones te lo encuentras por kits, por validaciones (ej precio = -99 significa en varios de mis sistemas ese producto no se puede vender a ese cliente ) se le llama valor centinela. Debería documentarse en el código pero no hay reglas de negocio en el código ni precauciones básicas de concurrencia que yo vi desde Clipper 5 en 1993.
O sea si la existencia es cero, estas documentando que alguien se llevo un medicamento que no tenias ?
vender controller.java
espera una buena conexion., a veces el cliente empieza en una pestaña y termina en otra. Su bucle de terminar venta viola los principios transaccionales, y el principio de atomicidad . Dos cajeros pueden vender elproducto al mismo tiempo. O dos clientes pueden por ejemplo en línea comprar boletos para un concierto.
float total = 0;
for (ProductoParaVender p: carrito) total += p.getTotal();
que puede llevar a errores de redondeo y que no cuadre la contabilidad o la venta del día o que no puedas timbrar una factura. Facturas rechazadas y multas por simplemente usar float.
venta.java
public Float getTotal() {
Float total = 0f;
for (ProductoVendido productoVendido : this.productos) {
total += productoVendido.getTotal();
}
return total;
}
Segun yo debió ser….
import java.math.BigDecimal;
import java.math.RoundingMode;
public BigDecimal getTotal() {
// Inicializamos con CERO absoluto, no con una aproximación binaria
BigDecimal total = BigDecimal.ZERO;
if (this.productos == null) return total;
for (ProductoVendido productoVendido : this.productos) {
// Obtenemos el total del producto (que también debería ser BigDecimal)
BigDecimal subtotal = productoVendido.getTotal();
// Sumamos con precisión exacta
total = total.add(subtotal);
}
// Establecemos la escala de salida (ej. 2 decimales para dinero)
return total.setScale(2, RoundingMode.HALF_UP);
}
y tendriamos que cambiar en productovendido.java // En ProductoVendido.java
public BigDecimal getTotal() {
// cantidad (ej. 1.500) * precio (ej. 10.50)
return this.cantidad.multiply(this.precio)
.setScale(2, RoundingMode.HALF_UP);
}
La base de datos la genera el ORM pero además está en https://github.com/parzibyte/sistema-ventas-spring-boot/blob/master/src/main/resources/esquema_ventas_springboot.sql
es my isam, no soportatransacciones ni acid e ignora muchas vecesllaves foraneas.
Los campos son float cuando deberian ser decimal (14,6);
CREATE TABLE `hibernate_sequence` (
`next_val` bigint(20) DEFAULT NULL
)
Usa un bigint para la secuencia, pero las tablas usan int(11). En el momento que la secuencia pase de 2,147,483,647, Hibernate intentará insertar ese valor en un int(11) y la base de datos colapsará. Es una inconsistencia de tipos clásica de «programador de tutorial».
Tipos de datos: Confirmado el desastre del FLOAT
fecha y hora deberia ser date time o timestamp. Yo me iriía por datetime
14 archivos de templates…. y es mas sano ue react
Analizando el pom.xml https://github.com/parzibyte/sistema-ventas-spring-boot/blob/master/pom.xml
Ni siquiera creo que sea de este proyecto. que hace aquí ?
no hay dependencia de jpa ni de mysql o maria db. spring-boot-starter-data-jpa El conector de MySQL o MariaDB.
No hay rastro de spring-boot-starter-security. Confirmado: Seguridad 0.
No hay librerías de precisión como Jackson-datatype-jsr310
El groupId dice org.springframework. Eso es un error de práctica. El groupId debería ser el dominio del autor (ej. me.parzibyte). Usar el de Spring es como si tú pusieras en la cabecera de tus archivos PHP que el autor es php.net.
que le falta al pom ? bueno…
Motor de persistencia.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
driver
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
La validacion de bean es una especificacion java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Parte 4. Como clonar el ejemplo parzibyte
Origen: https://github.com/parzibyte/sistema-ventas-spring-boot
Destino debian 13
No usarse en producción. Es muy buen ejemplo de frontend bien manejado y de back end que hace que te te corran o multen a la empresa o las dos cosas. En el siguiente tema explico porqué.
El código no lo hice yo. Es de calidad basnte buena para 2019 con sus puntos de falla y me sirve paraexplicar cosas en cursos de spring boot o de qarqkus.
Normalmente se prestaa dudas si es Gardle o Maven. Aqui estan los dos archivos. Gemini usa el maven pero puedes adaptarlo a usar el build de gradle.
Creado por GEMINI.
Repito no lo escribí yo.
Solo asegúrate de que al copiar el script de Bash al servidor, las comillas no se hayan convertido en «comillas tipográficas» (« » o “ ”) por el editor del blog, ya que Linux solo reconoce las comillas simples ' o dobles " rectas. Si el usuario copia las inclinadas, el script fallará.
Guía de Despliegue: Sistema de Ventas Spring Boot
1. Requisitos Previos en el Servidor
Antes de clonar, asegúrate de tener instaladas las herramientas base:
-
Java JDK 8: (Fundamental, ya que el
pom.xmlapunta a esta versión). -
Gradle y Maven: Para gestionar las dependencias y compilar. Parece que el real es GRADLE build.
-
MariaDB/MySQL: Funcionando.
Bash
# En Debian/Ubuntu
sudo apt update
sudo apt install openjdk-8-jdk maven mariadb-server
2. Espejo (Mirror) o Clonación a Gitea
Para tener el código bajo tu control en tu instancia de Gitea:
-
Entra a tu panel de Gitea.
-
Haz clic en el botón «+» y selecciona «Nueva migración».
-
En «URL de migración», pega:
https://github.com/parzibyte/sistema-ventas-spring-boot.git. -
Asígnale el nombre
sistema-ventasy guárdalo.
3. Clonación desde Gitea al Directorio de Trabajo
Ahora, desde la terminal de tu servidor, descarga el código desde tu repositorio:
Bash
cd /var/www/ # O tu ruta preferida
git clone http://tu-ip-gitea:3000/tu-usuario/sistema-ventas.git
cd sistema-ventas
4. Configuración de la Base de Datos
Debes crear la base de datos manualmente. Entra a MySQL:
SQL
CREATE DATABASE ventas_springboot;
Importante: Como vimos que el código usa Float y MyISAM, no intentes forzar InnoDB en este punto para evitar conflictos con el hibernate_sequence que trae el proyecto.
5. Configuración de Acceso (application.properties)
Este archivo es el que vincula el Back-End con la DB. Como el repo original podría no traerlo configurado para tu entorno, créalo:
Bash
nano src/main/resources/application.properties
Pega y ajusta lo siguiente:
Properties
spring.datasource.url=jdbc:mysql://localhost:3306/ventas_springboot?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=tu_contraseña_aqui
# ddl-auto=update creará las tablas automáticamente al arrancar
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=false
6. Compilación y Empaquetado
Aquí es donde Maven leerá el pom.xml y descargará los cientos de archivos que mencionamos:
Bash
# Limpia versiones previas y empaqueta el JAR saltando los tests
mvn clean package -DskipTests
Si todo sale bien, verás un mensaje de BUILD SUCCESS y un archivo generado en target/gs-rest-service-0.1.0.jar.
7. Ejecución del Sistema
Para correrlo de forma simple:
Bash
java -jar target/gs-rest-service-0.1.0.jar
Para dejarlo corriendo en segundo plano (como un servicio):
Bash
nohup java -jar target/gs-rest-service-0.1.0.jar > log_ventas.txt 2>&1 &
Script que puedes hacer paso por paso.
Este script hace todo el trabajo sucio: baja los cambios, compila, detiene la versión anterior y levanta la nueva.
1. El Script de Automatización (actualizar_sistema.sh)
Crea este archivo en la raíz de donde clonaste el proyecto:
Bash
#!/bin/bash
# — CONFIGURACIÓN —
APP_NAME=»sistema-ventas-springboot»
JAR_PATH=»target/gs-rest-service-0.1.0.jar»
LOG_FILE=»ejecucion_sistema.log»
echo «— Iniciando actualización de $APP_NAME —»
# 1. Bajar cambios de tu Gitea
echo «[1/4] Sincronizando con Gitea…»
git pull origin master
# 2. Compilar con Maven
echo «[2/4] Compilando y empaquetando (Maven)…»
mvn clean package -DskipTests
if [ $? -ne 0 ]; then
echo «ERROR: La compilación falló. Revisa el código.»
exit 1
fi
# 3. Detener el proceso anterior si existe
echo «[3/4] Deteniendo instancia anterior…»
PID=$(pgrep -f $JAR_PATH)
if [ -z «$PID» ]; then
echo «No había ninguna instancia corriendo.»
else
kill -9 $PID
echo «Instancia (PID: $PID) detenida.»
fi
# 4. Levantar la nueva versión
echo «[4/4] Levantando nueva versión…»
nohup java -jar $JAR_PATH > $LOG_FILE 2>&1 &
echo «— Proceso terminado con éxito —»
echo «Puedes ver el log en tiempo real con: tail -f $LOG_FILE»
Bonus Gitea actions simplificado
Para que este script funcione en tu servidor, el usuario (o tú) debe seguir estos pasos finales:
Dar permisos de ejecución:
Bash
chmod +x actualizar_sistema.sh
Ejecutarlo cada vez que haya cambios:
Bash
./actualizar_sistema.sh
3. El «Toque Maestro»: Webhook de Gitea (Opcional)
Si quieres que sea 100% automático (que al darle push desde tu laptop a Gitea, el servidor se actualice solo):
En Gitea, ve a la configuración de tu repositorio: Settings > Webhooks.
Añade un Webhook de tipo Gitea.
En la URL del objetivo, pon la dirección de un pequeño script que ejecute ./actualizar_sistema.sh con todas las ordenes anteriores
Parte 3 :Stack Lamp y Quarkus Dev
Tiempo para realizar lo mencionado aquí ? dos horas y con breaks.
Costo? Como anoté en las tres primeras partes esto fue un encargo para cliente. Desde el momento enb que entré a terminal en máquina limpia, hasta que cloné en la parte 5 fueron poco menos de tres días y no dedicados a esto..
La eficiencia técnica se traduce directamente en agilidad económica: mientras que el costo operativo de este stack en Vultr es de apenas 20 USD al mes, el proceso completo de implementación, desde el inicio de la configuración hasta la generación y clonación de la imagen final para el cliente, representó un gasto ínfimo de tan solo 2.33 USD. Esta cifra no solo cubre el tiempo de cómputo, sino también la transferencia de datos entre servidores durante el despliegue de la imagen maestra.
Es la prueba de que un servidor de alta densidad, basado en Debian 13 y optimizado quirúrgicamente, permite realizar despliegues masivos y entregas de infraestructura crítica por una fracción del costo de los proveedores tradicionales. No es solo gastar menos, es la capacidad de replicar entornos profesionales de producción en cuestión de minutos y por el precio de un café, manteniendo la soberanía total sobre el sistema y los datos del cliente.
En artículos anteriores levantamos con Debian un stack spring boot en un LAMP de 4gb y 80 disco duro. Quedé para terminar de poner un ejemplo con thymeleaf de un carrito de compras modificado de algo que está en internet sabiendo que hay limitaciones, pero como prueba de concepto.
Al mismo tiempo creo que es buena idea por una paridad por el manual de LEMP, levantar aquí Quarkus en modo dev, como prueba de concepto.
Al mismo tiempo hay tres ideas que me gustaría comentar antes para que se entienda mejor.
- Dentro de grupos tradicionales, de mi trabajo fuera de sistemas, se usa el término de COMPARTIR, IMPONER Y ORGANIZAR. Lo que estoy haciendo aqui y que hacen los de código libre como el del carrito de compras, es compartir. Si, tiene sus riesgos. Una persona decente evita IMPONER a menos que no le quede otro remedio. Pero en muchos trabajos te IMPONEN algo que no funciona y que se lleva a la empresa entre las patas y te acusan a ti. A mi me pasó solo una vez por el 2000 cuando trataron de usar PDO y access para mas de 50 usuarios simultáneos a pesar que les dije que access colapsa con 5 usuarios desde AÑOS antes. Mi trabajo es en muchos aspectos ORGANIZAR y tratar de arreglar desastres e incorporar nuevas solicitudes a un esquema simplificado.
- Con el paso del tiempo los sistemas se degradan y no solo por la obsolesencia programada o cambios de versiones. No sabes que basura le ponen al servidor después. O tu proveedor te empieza a hacer balooning. (dar menos memoria o cpu que la que les pagas) O cambios de proveedor porque el cliente quiere reducción de costos aunque sea 20% mas lento y 1% mas barato. Pero es importante que tengas presente que debes documentar que hiciste, que instalaste y que está. En código decir que versión de lenguaje o librería se usa.
- No es raro que alguien se equivoque si tiene dos servidores y trabaje en el servidor equivocado. Eso puede causar conflictos y a veces reinstalar todo el server. Por eso es que en muchos lugares no te quieren decir que tienen porque lo que tienen es miedo y no tienen ni idea de como lo hicieron funcionar después de un dedazo. «servidores en pincitas» digamos.
- Hay cosas que siempre fueron mala idea y nunca funcionaron aunque te digan lo contrario. En 2022 Tuve que alterar un sistema php 5.6 Laravel 5.2 hecho en 2017 que siempre ponía una frase en Alemán. La tecnología eraobsoleta ya entonces (2017). o cuando en 2006 me dieron un sistema en visual basic raro, y luego descubrí que habían sido dos programadores originales. Me dieron el código de uno y la base de datos de otro. Y aunque usé mucho del primero, cuando TRES años después aceptaron que me dieron de dos personas diferentes, me hubiera resultado mucho mejor haberlo hecho de cero con la base de datos existente porque uno usaba colecciones a lo bestia y el segundo diseñó para sql 2000 cuando el server real era sql 2007. Por favor… Verifica que versiones tienes de base de datos, sistema operativo , lenguajes de operación como primer paso. A lo mejor no te dejan verificarlo y trabajas doble pero mientras te pagan. Y pon por escrito plataforma destino y lo que te dijeron que tenían.
Asi que por completitud, vamos a instalar en debian 13 de 4 gb ram con LAMP el quarkus dev y veremos en otro tema lo del carrito de compras, notando problemas que tiene, aciertos que tiene y agradeciendo la aportación original, adaptandola a tiempos modernos y sin darle hardening. Es vital recordarle al lector que un código bajado de internet es un punto de partida, no un producto final para producción.
1 Quarkus:
De entrada es buena práctica de seguridad cambiar el puerto a algo diferente, Y en este caso es necesario. Por lo mismo como es «Modo Dev», recuerda que por defecto usa el puerto 8080, que ya tenemos ocupado por el «TestServer» de Spring. Por eso verás que uso 8081 en el comando de arranque:
Esto lo harás mas adelante.
# Arrancando Quarkus en el puerto 8081 para no chocar con Spring
./mvnw quarkus:dev -Dquarkus.http.port=8081
1.1. El Ajuste en Apache (El Puente)
Para que el mundo vea ese Quarkus en el 8081 a través de tu dominio, necesitamos añadir el puente en tu archivo de configuración SSL. Esto mantiene la coherencia con lo que hicimos antes:
Apache
# Añadir dentro de /etc/apache2/sites-available/tu-dominio.com-le-ssl.conf
# Proxy para Quarkus Dev Mode
ProxyPass /quarkus http://127.0.0.1:8081
ProxyPassReverse /quarkus http://127.0.0.1:8081
El fragmento te debe quedar así
# Proxy para el Backend de Spring Boot
ProxyPreserveHost On
ProxyPass /api/ http://127.0.0.1:8080/api
ProxyPassReverse /api/ http://127.0.0.1:8080/api# Proxy para Quarkus Dev Mode
ProxyPass /quarkus http://127.0.0.1:8081
ProxyPassReverse /quarkus http://127.0.0.1:8081
1.2. ¿Por qué «Modo Dev» en un servidor?
Es importante explicar que, aunque en producción usaríamos un ejecutable nativo, el modo Dev en el servidor nos permite:
- Live Coding: Cambias el código y los cambios se ven al refrescar el navegador (vía Apache).
- Dev UI: Quarkus levanta una interfaz increíble en /q/dev que facilita mucho la vida del programador.
- Pero… meterme en llas diferencia de modos de nativo y graal es mucho mas tardado. Así que dev sirve perfectamente para demostrar que Quarkus está instalado.
1.3. El Código «Hola Mundo» Ultraligero será el default
1.3.1 Preparar el terreno (Directorios y Permisos)
Como estamos bajo la filosofía de ORGANIZAR, vamos a crear una carpeta específica dentro de /var/www/ para que conviva con tus otros proyectos.
OJO : Quarkus no se «instala» en el sistema como un servicio tradicional (como un apt install).
Quarkus es un framework basado en Maven o Gradle. Lo que «instalas» es la estructura de tu proyecto y el ejecutable vive dentro de la carpeta que tú decidas. En nuestro caso, la «instalación» es el proyecto mismo que generamos.
# Crear la carpeta del proyecto
mkdir -p /var/www/quarkus-app
# Dar propiedad al usuario actual para poder trabajar sin sudo constante
chown -R $USER:$USER /var/www/quarkus-app
cd /var/www/quarkus-app
1.3.2. Reinstalando Maven y Gradle.
Estas dos herramientas siempre sirven. Teóricamente tienes Maven por Spring, pero casi siempre instalo los dos para detectar y solucionar problemas de permisos, versiones, y gradle es mi preferido aunque sea antiguo porque sufre menos corrupción y menos afectado por problemas de permisos. Uno es un camión de carga (maven) y el otro es un sedán con buen motor y que consume menos ram (gradle). Es mi punto de vista.
Tratar de usar java springboot o quarkus sin maven o sin gradle es como tratar de tomar agua sin vaso y sin manos.
# Actualizamos repositorios
apt update# Instalar Maven (El estándar para la mayoría de proyectos Spring/Quarkus)
apt install maven -y# Verificar Maven
mvn -version
Para Gradle, si prefieres tenerlo disponible (muchos devs de la «Tribu Spring» lo aman por ser más rápido que Maven):Bash
apt install gradle -y
# Verificar Gradle
gradle -v
1.3.3. Generar el proyecto Quarkus de cero
Fijate que uso un directorio con mis iniciales. Usa lo que tu quieras.
En lugar de descargar un ZIP, usaremos el comando de Maven (que ya deberías tener por el paso de Spring o la reinstalación del paso previo) para generar la estructura básica. Esto garantiza que se cree el directorio com/aoa/ que mencionamos:
mvn io.quarkus.platform:quarkus-maven-plugin:3.6.4:create \
-DprojectGroupId=com.aoa \
-DprojectArtifactId=monitoreo-quarkus \
-DclassName=»com.aoa.GreetingResource» \
-Dpath=»/quarkus»
cd monitoreo-quarkus
La primera vez que corres el comando de generación, Maven descargará medio internet. No te asustes. Es el «costo de entrada» para tener todas las dependencias organizadas. Si eres como yo, quizá te den ganas de llorar al ver TODO lo que se instala para un hola mundo. Se positivo, velo como «area de oportunidad» o «menos competencia»
Verás un letrero con letras verdes diciendo BUILD SUCESS. Si no lo ves algo hiciste mal y no debes proseguir.
Antes de que el usuario intente arrancar Quarkus en el punto 1.3.3, necesita saber que el archivo de configuración debe estar listo, de lo contrario chocará con Spring. Puedes añadir esto:
1.3.4 El ADN del proyecto (Puertos)
Entra a la carpeta y dile a Quarkus quién es y por dónde debe hablar:
Asegúrate de que tenga estas líneas para que Apache pueda encontrarlo… Añade estas líneas para asegurar la paz con Apache y Spring:
- mkdir -p src/main/resources
- nano src/main/resources/application.properties
Properties
quarkus.http.port=8081
quarkus.http.host=127.0.0.1
Recuerda que siempre debes Verificar y Recargar Apache
Antes de reiniciar, siempre es bueno verificar que no dejamos algún error de dedo (como un espacio antes de <VirtualHost> o una comilla curva de WordPress).
- # 1. Test de configuración (Busca el «Syntax OK»)
- apache2ctl configtest
- # 2. Si todo está bien, recargamos
- systemctl restart apache2
1.3.5 El problema de las versiones.
Recuerdas que dijimos que tenemos SDK 21 ? Pues Maven, Gradle, tus compañeros tu jefe o lo que sea pueden estar seguros que el servidor no tiene lo que quieres. Y eso debes primer asegurarte que es lo que tienes. Aqui tenemos SDK 21. Pero Maven puede que tenga una versión diferente.
Revisa con Maven que tienes :
- mvn -version
Te debe decir que tienes 21.0.9 Si no busca en internet y revisa por si las dudas en que servidor estás.
Si estás en el servidor correcto y debes corregirlo empezamos antes que nada, por editar el pom.xml de maven, su configuración. o vas a obtener un feo letrero rojo que diga «release version 21 not supported»
La solución rápida (El ajuste en el pom.xml)
Debemos decirle explícitamente a Maven que use la versión 21. Entra al archivo de configuración de tu proyecto:
- cd /var/www/quarkus-app/monitoreo-quarkus
- nano pom.xml
Busca la etiqueta <properties> (está al principio) y asegúrate de que estas dos líneas digan 21:<maven.compiler.release>21</maven.compiler.release>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>¿Por qué pasa esto?
Como puse antes: «Verifica que versiones tienes… como primer paso». Aquí tienes el ejemplo vivo. Tienes el JDK 21 instalado (la máquina), pero Maven está tratando de usar un nivel de lenguaje que no reconoce o que cree que no está soportado por el compilador actual.Bonus:
- Una vez me pasó hace años que estaban tratando de echar a andar quarkus en un servidor equivocado. Lo instalaron en uno y se queajban que no existia la carpeta de archivos.
Segundo paso : «Verifica que versiones tienes… y en que servidor estás como segundo paso»
1.3.6 El momento de la verdad (Arrancar Quarkus)
la diferencia entre texto inútil y código, son los permisos.
Porque ?
A veces alguien mete mano. En windows lo hace el sistema operativo mismo. Pero si no eres el que usa el server en exclusiva, se precavido y si no también.
- cd /var/www/quarkus-app/monitoreo-quarkus
- chmod +x mvnw
A veces el BUILD SUCCESS de la generación del proyecto nos miente dándonos una falsa sensación de seguridad. El wrapper (mvnw) es un script de shell, y en Linux, un script sin permiso de ejecución es solo un archivo de texto inútil. No olvides el chmod +x mvnw o te quedarás mirando la pantalla sin entender por qué el comando no hace nada cuando ayer si funcionaba.
Ahora que Apache ya tiene el puente construido hacia el puerto 8081, solo falta que alguien esté escuchando del otro lado. Corre el comando que ya tenías:
- cd /var/www/quarkus-app/monitoreo-quarkus
- ./mvnw quarkus:dev -Dquarkus.http.port=8081
Esto es vital. Cuando corres Quarkus en una terminal de un servidor remoto (vía SSH), a veces el modo dev intenta abrir una ventana o se queda esperando una entrada de teclado. Además, necesitamos que acepte conexiones externas (a través de Apache).
¿Por qué hacerlo así?
Al crear el directorio com.aoa mediante el plugin de Maven, ya tienes un archivo llamado GreetingResource.java listo para ser modificado. No tienes que crear rutas manualmente.
1.3.7 Verificación Final de Quarkus
Si todo ha salido bien, al final de la lluvia de logs y de descargar la media mitad de internet que te faltaba verás un mensaje que dice: Listening on: http://127.0.0.1:8081
Ahora, la prueba de fuego no es entrar por el puerto 3000 o 8081 directamente (que deberían estar cerrados en el firewall), sino a través de nuestro puente blindado:
Abre en tu navegador: https://tu-dominio.com/quarkus
Deberías ver una página que dice «Hello from Quarkus REST» (o lo que hayas puesto en tu GreetingResource.java).
¿Por qué es esto un éxito?
Atravesaste el Proxy de Apache: Tu servidor web principal está redirigiendo el tráfico correctamente.
SSL funcionando: Entraste por HTTPS sin errores de certificado.
Aislamiento de puertos: Tienes Spring en el 8080 y Quarkus en el 8081 conviviendo en paz en el mismo Debian 13.
La pantalla debe ser como esto:
así que por la módica cantidad de 20 USD o menos tienes instalado springboot php lamp quarkus maven y gradle, e incluso con 10 USD puedes en 2 gb ram para pruebas.
Una llamada al monitoreo ahora nos lleva a esto, que significa en palabras comunes que para pruebas es suficiente con 2 gb de ram te digan lo que te digan las LLM u otras personas de sistemas. Si usas Un stack decente como Debian 13.
Estado del Servidor (Debian 13)
Memoria RAM: 1475 MB usados de 3916 MB (37.67%)
Disco Duro: 15.32 GB usados de 74.56 GB (20.54%)
Carga de CPU (1, 5, 15 min): 0, 0.052734375, 0.0595703125
Que te parece horrible y maravilloso y te hace dudar del futuro de internet ? Si. No es posible que necesites 1475 MB de ram para un hola mundo. Además de una conexión estable y descargar varios gigabytes.
Y por eso muchos programadores que van poniendo basura tras basura en una base sucia, acaban cambiando de profesión. Las buenas prácticas te permiten proseguir, pero muchas empresas gastan demasiado por malas prácticas, y malas elecciones de framework o demás.
No van a durar mucho en México empresas medianas que usen la nube para esto. Si ves que hay cuatro o cinco personas que no hacen nada y les pagan, la empresa quiza puede. Pero esto es igual o mas costoso en la nube que un programador bueno y a veces implementado peor. Preocúpate cuando haya problemas para pagar nómina o nube.
No me extrañaría que se cambie a algo más en 2029, en lugar de springboot que es 2014. Sea Quarkus Javelin o algo mas que salga. Springboot será legacy.
El carrito de compras lo pondré en parte 4.
Cine de terror y servidores
He comentado que crecí solamente con mi papá desde los diez meses de edad. Volví a ver a mi mamá eventualmente a eso de los siete años y solo los vi juntos una vez en mi vida (cinco minutos) entre 1982 y 1984. Mi papá lo manejó tan bien que, durante muchos años, no me di cuenta de que otras familias tenían papá y mamá. Y qué bueno que me quedé con él después del divorcio, por cierto.
Pero una de las cosas que hacía mi papá era llevarme, desde los seis o siete años(1978-1979) aprox, a ciclos de películas de todo tipo en los cines Bella Época y Elektra. Comprabas un abono y podías ver unas 30 películas diferentes en una misma semana. Así que, cuando no viajaba con él por trabajo, después de comer y ver qué tarea había, tocaba ir los dos al cine. Fácilmente estuve ahí unas veinte semanas al año.
Me tocó ver varias veces ciclos de películas de terror de todo tipo. No las infames de Ed Wood, pero sí todas las de Nosferatu, Frankenstein, Drácula, La cosa y El bebé de Rosemary. Cuando vinieron mis primas Marcela y Angélica de Guadalajara, yo tenía unos ocho años y él nos llevó a los tres a ver como cinco películas de terror adicionales en el Bella Época. Mis primas debían de tener unos 9 y 12 años.
El caso es que hoy, buscando información para un cliente al que tengo que ir a ver, vi una fotografía y me acordé de un 30 de abril donde vi unas películas infames. Ya las busqué y sí existen: El terror del pueblo chico, que era literalmente un western musical actuado por enanos; y una llamada Los marcianos se llevaron a Santa Claus. Y era SERIA no era comedia. Sí, no las imaginé.
Entre esos recuerdos hay secuencias que a uno NO se le olvidan. La surrealista escena de los enanos en el bar parece de pesadilla pero, sí, no la imaginé.
https://en.wikipedia.org/wiki/The_Terror_of_Tiny_Town
Y hubo otras secuencias. Por ejemplo, recuerdo varias de películas de terror. En una, unas mujeres vueltas vampiro eran reclutadas a lo Charles Manson, con medallones que les permitían moverse de día en centros comerciales y negaban a sus familiares. U otras de terror ambientadas en cazas de brujas en la Inglaterra de 1600 y en Salem, incluyendo hachazos, sorpresas y vampiros. No eran malas; no era comedia de terror, sino terror para adultos en lo que cabe.
Se me pasaba decir que el género de películas no era terror tal cual, sino algo que los italianos llaman giallo, que es una mezcla de terror ,slasher, thriller, etc y no solo italiano. Por ejemplo, de la del Dr. Phibes me acuerdo bien por las motivaciones absolutamente creíbles y por unos cinco asesinatos de los que tengo presentes unos tres. A la que matan desde el piso de arriba con el ácido, por ejemplo.
https://en.wikipedia.org/wiki/The_Abominable_Dr._Phibes
https://en.wikipedia.org/wiki/Giallo
Aunque parezca que eran puras de terror, no. También me tocaron las películas de Mario Lanza y Fred Astaire. No sé cuántas veces vi Las siete colinas de Roma. O las de El Gordo y el Flaco (Fra Diavolo, por ejemplo). O películas de los Barrymore. Musicales de Howard Keel. El Ladrón de Bagdad. Beau Geste. Películas de esa época: El prisionero de Zenda, Shane, el desconocido.
¡¡¡Shane!!!
El ciclo de películas llegó a su fin más o menos en 1982. Recuerdo que por entonces fui a ver una con mi papá llamada Happy Birthday to Me, que aunque tenga reseñas negativas, la volví a ver y era buena película. Dos o tres muertes memorables; el argumento no era predecible, ni entonces a los 10 años ni a los 35 cuando la volví a ver. Pero para esas alturas, creo que fue la última película de terror que vi. Sé de qué van Pesadilla en la calle del infierno, Chucky y todas esas, pero me dan flojera.
El caso es que, en 1982, había visto más películas de terror (giallo) y clásicas que nadie que yo supiera a mi edad. Dificultades financieras en casa, más cambios en la política de los abonos y la programación, hicieron que a mediados de 1983 dejáramos de ir a esos cines. Y creo que en el 1982 ya no me dejabanentrara a películas de terror por nuevas leyes. Todavía recuerdo, en 1984, haber visto en el Elektra cinco veces seguidas The Last Starfighter. ¡Peliculón de ciencia ficción!
¿Qué tiene que ver esto con servidores? Pues el surrealismo.
Literalmente, me he encontrado con que hablen de un «superservidor» con un GB de RAM en 2010 por el que pagaban 1500-1600 USD mensuales (20 mil pesos) en la empresa de satélites (el de la guitarra eléctrica), o cómo cuando he tenido que arreglar desastres de microservicios que me recordaban al Mago de Oz y al western de los enanos.
He revisado mucho código fuente ajeno, muchos documentos de Word y Excel creados por profesionistas, pero hay una sensación de sorpresa que me da a veces descubrir servidores ajenos. Como dato, en el año 2012 rescaté de un rancho a dos cachorritas de perro de 15 días, antes de que las matara su papá, y las tuve conmigo hasta su muerte por edad. Una de ellas era una chihuahua miniaturizada y con pelo estilo diente de león. De chiquita parecía otra cosa. Pero dos veterinarios, al verla, dijeron: «¿Y esto qué es?».
Así me pasa al ver ciertos servidores.
Seguiré las guías mañana. Tengo dos tareas de la universidad y que ir a cobrarle a un cliente en el mundo real.
Parte 2 levantar un lamp con gitea y springboot
El proceso mencionado aqui tomó cuatro horas aprox.
Costo? Como anoté en las tres primeras partes esto fue un encargo para cliente. Desde el momento enb que entré a terminal en máquina limpia, hasta que cloné en la parte 5 fueron poco menos de tres días y no dedicados a esto..
La eficiencia técnica se traduce directamente en agilidad económica: mientras que el costo operativo de este stack en Vultr es de apenas 20 USD al mes, el proceso completo de implementación, desde el inicio de la configuración hasta la generación y clonación de la imagen final para el cliente, representó un gasto ínfimo de tan solo 2.33 USD. Esta cifra no solo cubre el tiempo de cómputo, sino también la transferencia de datos entre servidores durante el despliegue de la imagen maestra.
Es la prueba de que un servidor de alta densidad, basado en Debian 13 y optimizado quirúrgicamente, permite realizar despliegues masivos y entregas de infraestructura crítica por una fracción del costo de los proveedores tradicionales. No es solo gastar menos, es la capacidad de replicar entornos profesionales de producción en cuestión de minutos y por el precio de un café, manteniendo la soberanía total sobre el sistema y los datos del cliente.
El día de hoy terminaré de instalar este servidor Apache (LAMP) al tiempo que actualizo en otro ya existente, los tres sobre Debian 13. Hay una relación importante entre la memoria y el consumo de ella, y por qué usar Gitea. Joel Spolsky, antiguo programador de Microsoft, hablaba de las doce preguntas y una de ellas tenía que ver con builds / compilaciones de un solo paso. Creo que una vez establecido el front end (en este caso React) no necesitas hacerle muchos cambios, pero sí es común que establezcas nuevas funcionalidades «bajo el capó», en backend, que no se ven. Y en eso Quarkus brilla. Además, usa mucho menos memoria que Spring Boot.
Sin embargo, tengo que hacer una reflexión personal. Casi siempre mi trabajo ha sido arreglar desastres. A veces programando de cero, a veces dando mantenimiento a cosas indescriptibles. He visto consumos gigantescos de hosts que no servían, incluyendo transas de las personas de sistemas. Tecnologías corruptas o mal empleadas.
Usando una metáfora, yo no necesito un iPhone. Uso un teléfono Motorola G31 de unos 150 USD. La principal razón es porque tiene el encendido por huella dactilar, sin presionar botón lateral. Un iPhone o iPad solo me interesaría por las fotos de alta calidad, por documentos de juzgados.
Sin embargo, a nadie lo corrían hace años por contratar IBM. Ahora es algo parecido con Azure y AWS. Principalmente en entorno Azure he visto que universidades públicas reciben dinero para promover Azure y sus recién egresados piensan que es lo único. Usando una metáfora… no todos necesitan un Samsung de alta gama o un iPhone. Lo mismo pasa con Azure y AWS.
En este entendido, a veces el presupuesto o vivir en la punta del cerro, aunque puedas y lo quieras, te complican muchísimo comprar un teléfono de alta gama. Y para estar con línea de cobre, sin línea óptica. Otro ejemplo sería que salir con un billete de 1000 MXN a la calle es como traer nada, porque cambiarlo o que te lo acepten es difícil, y debe ser horrible salir a la calle con 3000 pesos en monedas de a peso. Como dato particular, en viajes a la playa solía usar una bolsa agarrable a la pierna, llena de monedas de 10 pesos, el equivalente de 3000 MXN, y se podía mojar.
Muchos de tus clientes de correo o de host son personas que usan pocos recursos. No falta la princesa o el princeso que si no es iPhone 25 no te mira a los ojos. Pues yo creo que salir a la calle con billetes de 100 y 200 te da movilidad. Se trata de resolver un problema, y Quarkus es maravilloso en eso. Pero si sumas su bajo consumo de memoria con relación a Spring Boot, te encuentras con la facilidad de implementación que hablaba Spolsky. Y eso solo te lo da en Web React / Quarkus, o PHP puro.
Pero en este caso el cliente quiere Springboot.
Asi que empecemos:
Ojo con dos errores comunes por ser wordpress este texto, y lo puse en letras grandes para que no haya duda.
-
Las Comillas: Al copiar y pegar en editores como WordPress, las comillas rectas » a veces se convierten en curvas “. Asegúrate de que en los bloques de código (especialmente el de Java y Nginx) se mantengan las comillas rectas, o el compilador dará error.
- Permisos de Ejecución: En la parte donde creas el servicio systemd, debes asegurarte de que el archivo mvnw tenga permisos de ejecución (chmod +x mvnw). Es un detalle que a veces olvidamos.
Los 3 puntos clave de hoy para Gitea:
- Proxy Inverso (Apache): Despedirnos del puerto 3000 para entrar por git.tu-dominio.com.
- SSL Final: Extender el certificado de Certbot para que el subdominio de Gitea y la ruta /app/ de React sean inexpugnables.
- Ajuste del app.ini: Configurar internamente a Gitea para que reconozca su nueva identidad HTTPS y cerrar el puerto en el firewall.
Es como poner carrocería y blindaje al servidor. Digamos que es un hardening de nivel medio.
En lugar de entrar por una subcarpeta, vamos a darle su propia identidad: git.tu-dominio.com.
Empezamos !!!
Verificamos que este instalada la opcion de Proxy.
-
- a2enmod proxy
- a2enmod proxy_http
- systemctl restart apache2
Primero, crea el archivo de VirtualHost para el subdominio:
nano /etc/apache2/sites-available/git.tu-dominio.com.conf
Y como contenido:
<VirtualHost *:80>
ServerName git.tu-dominio.com
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
ErrorLog ${APACHE_LOG_DIR}/gitea-error.log
CustomLog ${APACHE_LOG_DIR}/gitea-access.log combined
</VirtualHost>
Habilitamos el sitio: a2ensite git.tu-dominio.com.conf && systemctl restart apache2
Pero ahora hay que decirle a certbot que cree un certificado.
-
certbot --apache -d git.tu-dominio.comCon un f5 ya se debe de ver con candado de certificado (en mis tres casos si)
Si no entra probablementees porque no tienes debian 13.
Si Certbot te pregunta si quieres «Redirigir» (Redirect), dile que SÍ (opción 2). Esto insertará automáticamente las reglas necesarias para que cualquier intento de entrar por HTTP salte a HTTPS.
2. Verificar el archivo generado
Una vez que Certbot termine, debería haber creado un archivo nuevo en
/etc/apache2/sites-available/git.tu-dominio.com-le-ssl.conf.Si entras y sigue sin cargar, revisa que ese nuevo archivo tenga las líneas del Proxy, porque a veces Certbot solo copia lo básico. El archivo debería verse así:
Apache
<IfModule mod_ssl.c> <VirtualHost *:443> ServerName git.tu-dominio.com ProxyPreserveHost On ProxyPass / http://127.0.0.1:3000/ ProxyPassReverse / http://127.0.0.1:3000/ # Las líneas de los certificados (que pone Certbot) SSLCertificateFile /etc/letsencrypt/live/git.tu-dominio.com/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/git.tu-dominio.com/privkey.pem Include /etc/letsencrypt/options-ssl-apache.conf </VirtualHost> </IfModule>
3. ¿Por qué podría seguir fallando? (Checklist de purga)
DNS: ¿El subdominio
gitya apunta a la IP de tu servidor? (Si el ping agit.tu-dominio.comno responde, Certbot fallará).A2ensite: Si Certbot creó el archivo pero no lo activó:
a2ensite git.tu-dominio.com-le-ssl.confsystemctl restart apache2Firewall: Recuerda que ahora entramos por el 443. Asegúrate de que UFW lo permita:
ufw allow https
Si ya quedó falt algo importante.
Cerrar el puerto 3000
- #Falta este paso crucial:
- ufw delete allow 3000/tcp
- ufw reload # Agregar esto
Estaremos listos para instalar Java 21 y Spring Boot.
Primero, recordemos que es una LTS.
Como dije instalaremos una versión LTS (Long Term Support). En Debian 13, la opción más robusta y compatible con las versiones modernas de Spring Boot 3.x es Java 21.
nano /var/www/tu-dominio.com/public_html/monitoreo.php
Pega el siguiente código (quiza mejor si lo pegas en un notepad primero y sin espacios al principio ….):
Debe quedar claro que a veces los servidores tienen cosas cerradas. Asi quye te voy a poner dos opciones:
<?php
// Obtener datos de memoria desde /proc/meminfo o el comando free
$free = shell_exec('free -m');
$free_lines = explode("\n", trim($free));
$mem_info = preg_split('/\s+/', $free_lines[1]);
$total_mem = $mem_info[1];
$used_mem = $mem_info[2];
$percent_mem = round(($used_mem / $total_mem) * 100, 2);
// Obtener datos de disco
$disk_total = disk_total_space("/") / 1024 / 1024 / 1024; // GB
$disk_free = disk_free_space("/") / 1024 / 1024 / 1024; // GB
$disk_used = $disk_total - $disk_free;
$percent_disk = round(($disk_used / $disk_total) * 100, 2);
echo "<h2>Estado del Servidor (Debian 13)</h2>";
echo "<b>Memoria RAM:</b> $used_mem MB usados de $total_mem MB ($percent_mem%)<br>";
echo "<div style='width:300px; background:#ddd;'><div style='width:$percent_mem%; background:red; height:10px;'></div></div>";
echo "<br><b>Disco Duro:</b> " . round($disk_used, 2) . " GB usados de " . round($disk_total, 2) . " GB ($percent_disk%)<br>";
echo "<div style='width:300px; background:#ddd;'><div style='width:$percent_disk%; background:blue; height:10px;'></div></div>";
echo "<br><b>Carga de CPU (1, 5, 15 min):</b> " . implode(", ", sys_getloadavg());
?>
Opcion 2 :
- nano /var/www/tu-dominio.com/public_html/monitoreoaoa2.php
<?php
// Habilitar errores para ver qué pasa si falla
ini_set(‘display_errors’, 1);
error_reporting(E_ALL);function get_server_memory_usage(){
$free = shell_exec(‘free’);
if(!$free) return «Error: No se pudo ejecutar ‘free’.»;
$free = (string)trim($free);
$free_arr = explode(«\n», $free);
$mem = explode(» «, $free_arr[1]);
$mem = array_filter($mem);
$mem = array_merge($mem);
$memory_usage = $mem[2] / $mem[1] * 100;
return array(‘total’ => $mem[1], ‘used’ => $mem[2], ‘percent’ => round($memory_usage,2));
}$mem = get_server_memory_usage();
echo «<h3>Uso de RAM: » . $mem[‘percent’] . «%</h3>»;
?>Eso me dio la opcion uno y la segunda un texto de 18% libre
Creo que ahora puees ver porqué puedo correr muchísimas cosas en servidores de 1 a 2 GB de ram, en php puro, aunque sean para 20-30 usuarios simultáneos.
Mantenimiento: La regla de oro antes de la batalla
Antes de meter el motor de Spring Boot en la siguiente entrega, hay que asegurarse de que los cimientos estén firmes. Instalar Gitea, configurar Proxies y mover Certbot genera movimiento en los repositorios. Un buen administrador no deja basura atrás.
Corremos una actualización final para consolidar los parches de seguridad de los módulos de Apache y las librerías de SSL que acabamos de configurar:
- apt update && apt upgrade -y
Y para mantener ese 19% de RAM y el disco limpio, eliminamos cualquier residuo de paquetes que ya no sea necesario tras nuestras configuraciones:
- apt autoremove -y
- apt autoclean
En mi caso solo el servidor que venía de debian 11 hizo cambios quitando paquetes.
Hasta aquí es lo que llego hago normalmente en mis servidores PROPIOS y a veces ni les instalo Gitea.
Despliegue de Spring Boot, parte Uno.
EL mejor ejempl oque se me ocurre aquí es que de repente le puedes pedir a tu hija que vaya a la tienda, oa un compañero que saque una copia o un respaldo. Si eres preofesional lo rveisas despues porque no puedes hacer desayuno sin ir a la tienda, ni el tramnite sin la copia.
Parte 3.1 : El Despliegue de Spring Boot (JDK 21)
1. Instalación del Motor (JDK 21)
Primero, instalamos el entorno de ejecución para Java. Usamos la versión 21 por ser la LTS actual y necesaria para Spring Boot 3.
- apt update
- apt install openjdk-21-jdk -y
- # Verificamos la instalación
- java -version
2. Preparación del Directorio de Trabajo
- No debemos correr el backend como root. Usaremos /var/www para mantener la estructura de Apache.
Bash
- mkdir -p /var/www/backups_java
- # Le damos la propiedad al usuario del servidor web
- chown www-data:www-data /var/www/backups_java
- chmod 755 /var/www/backups_java
3. El Script de Lanzamiento (El «Limitador de RAM»)
Para que Spring Boot no se «coma» todo el servidor (manteniendo nuestra filosofía de ahorro), crearemos un pequeño script de arranque que limite la memoria.
nano /var/www/backups_java/start.sh
Teb cuidado que no hayan espacios en blanco al inicio del archivo o en un primer renglon.
#!/bin/bash
# Limitamos a 512MB de RAM máxima para mantener la movilidad
java -Xms256M -Xmx512M -jar /var/www/backups_java/tu-proyecto.jar
No olvides el permiso de ejecución:
- chmod +x /var/www/backups_java/start.sh
4. Creación del Servicio Inmortal (Systemd)
Para que el backend inicie solo con el servidor y se reinicie si falla.
- nano /etc/systemd/system/spring-app.service
y pones dentro
[Unit]
Description=Backend Spring Boot
After=syslog.target network.target mariadb.service[Service]
User=www-data
WorkingDirectory=/var/www/backups_java
ExecStart=/var/www/backups_java/start.sh
SuccessExitStatus=143
Restart=always
RestartSec=10[Install]
WantedBy=multi-user.target
Despues de eso …
- # Recargar systemd para que reconozca el nuevo servicio
- systemctl daemon-reload
- # Habilitar para que inicie con el servidor y arrancarlo ahora
- systemctl enable spring-app
- systemctl start spring-app
5. El Puente en Apache (Proxy para la API)
Finalmente, le decimos a Apache que todo lo que llegue a tu-dominio.com/api lo mande al motor de Spring Boot (puerto 8080).
Edita tu archivo de VirtualHost SSL existente:
- nano /etc/apache2/sites-available/tu-dominio.com-le-ssl.conf
Agrega estas líneas antes del cierre de </VirtualHost>:
# Proxy para el Backend de Spring Boot
ProxyPass /api http://127.0.0.1:8080/api
ProxyPassReverse /api http://127.0.0.1:8080/api
6 Ahora verificamos :
apache2ctl configtest
debe darte syntax ok
Vamos a probar : URL Qué debe mostrar Archivo que lo controla
- tu-dominio.com tu prueba que hiciste o el mismo monitoreo done lo hayas puesto.
- tu-dominio.com/app/ El React (Front) tu-dominio.com-le-ssl.conf (por eso es el app en virtualhost)
- git.tu-dominio.com El Gitea (Repos) git.tu-dominio.com-le-ssl.conf
- tu-dominio.com/api Error 503 (por ahora) El bloque Proxy que añadiremos
7 Ahora nos preparamos para el hola mundo en spring :
Nota: debe de estar corriendo cada diez segundos tratando de lanzar el rpoyecto que mencionamso que todavia no existe. asi que …
7.1. Crear el código fuente de prueba
Vamos a crear un servidor web básico en un solo archivo Java dentro de tu carpeta de trabajo:
nano /var/www/backups_java/TestServer.java
Pega este código (es Java 21 estándar, no requiere librerías externas):
/* Prueba en java 21 */
import com.sun.net.httpserver.HttpServer;
import java.io.OutputStream;
import java.net.InetSocketAddress;
public class TestServer {
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
server.createContext("/api", (exchange) -> {
String response = "<h1>Backend Skeleton: OK</h1><p>Conectado exitosamente via Apache Proxy</p>";
exchange.sendResponseHeaders(200, response.length());
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.close();
});
System.out.println("Servidor de prueba iniciado en el puerto 8080...");
server.setExecutor(null);
server.start();
}
}
Paso 7.2: Compila y genera el JAR:
cd /var/www/backups_java/
javac TestServer.java
# Creamos el JAR con el punto de entrada en TestServer
jar cfe tu-proyecto.jar TestServer TestServer.class
Paso 7.3 Permisos y Arranque:
- chown www-data:www-data tu-proyecto.jar
- systemctl restart spring-app
Paso 7.4
Desde terminal o bash
- #prueba 1
- journalctl -u spring-app -f # te debe mosttar servidor de prueba iniciado en el puerto 8080
- #Prueba 2
- curl -v http://127.0.0.1:8080/api
-
y https://tu-dominio.com/api/
si la tres no queda…. edita el archivo le-ssl de tu dominio que esta en /etc/apache2/sites-available/
y que qudee asi … las /api/ son importantes la barra, es una forma de forzar a apache a hacer que hagas lo que quieres y si te fijas cambiamos el orden tambien.
<IfModule mod_ssl.c>
<VirtualHost _default_:443>
ServerName tu-dominio.com
ServerAlias www.tu-dominio.com
ServerAdmin tumail@gmail.com# Proxy para el Backend de Spring Boot
ProxyPreserveHost On
ProxyPass /api/ http://127.0.0.1:8080/api
ProxyPassReverse /api/ http://127.0.0.1:8080/api# Apuntamos a la vitrina real
DocumentRoot /var/www/tu-dominio.com/public_html<Directory /var/www/tu-dominio.com/public_html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory># El motor de React
<Directory /var/www/tu-dominio.com/public_html/app>
RewriteEngine On
RewriteBase /app/
RewriteRule ^index\.html$ – [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /app/index.html [L]
</Directory>ErrorLog ${APACHE_LOG_DIR}/tu-dominio-error.log
CustomLog ${APACHE_LOG_DIR}/tu-dominio-access.log combinedInclude /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/tu-dominio.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/tu-dominio.com/privkey.pem
</VirtualHost>
</IfModule>
Y luego otra vez
- # 1. Asegura que los módulos estén activos (por si acaso)
- a2enmod proxy proxy_http
- # 2. Verifica que no haya errores de dedo en el archivo
- apache2ctl configtest
- # 3. Reinicia Apache para que lea la nueva prioridad
- systemctl restart apache2
Ahora si …
- https://tu-dominio.com/api/
La diagonal es importante y debes ver …
Backend Skeleton: OK
Conectado exitosamente via Apache Proxy
¿Por qué funcionará ahora?
Al poner el Proxy antes del DocumentRoot, le dices a Apache: «Antes de que busques archivos en la carpeta public_html, si ves que la dirección empieza con /api, mándala directo a Java».
En Apache, omitir una diagonal es la diferencia entre que el servidor busque un archivo o que entienda que es un puente hacia otro servicio. No es capricho, es sintaxis.
Finalmente :
https://tu-dominio.com/monitoreo.php
Como puedes ver los numeros son excelentes. Lo que pesa es el codigo, y la mezcla de tres modos diferentes de usar Spring Boot. Debes tratar de unificarlo en uno solo. Yo prefiero JDBC pero hibernate / JPA tiene sus ventajas.
Vamos a comparar los dos graficos.
Antes de Spring
Despues de spring
En realidad estas usando menos de 40 MB mas.
8.- Check list
- # ========================================
# CHECKLIST FINAL – PARTE 2 COMPLETA
# ======================================== - # Gitea en subdominio con SSL
curl -I https://git.tu-dominio.com | grep «200 OK» - # Puerto 3000 cerrado externamente
nmap -p 3000 $(curl -s ifconfig.me) # Debe dar «filtered» o «closed» - # Java 21 instalado
java -version | grep «openjdk version \»21» - # Skeleton respondiendo
curl https://tu-dominio.com/api/ | grep «Backend Skeleton» - # Memoria bajo control
- free -m | awk ‘NR==2{printf «RAM: %s/%sMB (%.2f%%)\n», $3,$2,$3*100/$2 }’
free -m | awk ‘NR==2{printf «RAM: %s/%sMB (%.2f%%)\n», $3,$2,$3*100/$2 }’
# Debe mostrar ~788-820 MB de 4096 MB (~20%) -
(Asegúrate de que las comillas simples son rectas en WordPress. Las centrales son dobles chr(34))# Servicio
-
spring-app activo
systemctl is-active spring-app # Debe decir «active» - # Logs sin errores críticos
journalctl -u spring-app –since «10 minutes ago» | grep -i «error»
Cual es el problema REAL ?
Que no tienes instalado así en tus servers de producción. Solo una personao varias que hicieron lo que buenamente podían.
Un servidor es tan bueno como su admin
Cual es mi preocupación moderna ?
Tenemos un hola mundo que usa 788 mb de ram y tres o cuatro horas de un devops experto para configurar.
Y cuando llegan los programadores… mezclan los estandares y hay errores de diseño.
Usando metáforas… hay coches automáticos y hay coches estandard. También algunos tienen diferente el cambio de velocidades. Algunos a 50 otros a 60 kms Pero debe haber un estandard de los programadores para no dañar la maquinaria. De nada sirve tener un motor afinado si el conductor no sabe meter las velocidades y que es criminal en un automático pisar a la vez el freno y el acelearador. Con lo que yo hice, no necesitas meter el clutch. Solo no hacer barbaridades.
o con mi sinceridad habitual: Los programadores mezclan los estándares y hay errores de diseño… De nada sirve tener un motor afinado si el conductor no sabe meter las velocidades
El Problema Real del Spring Boot Salvaje:
- Equipos mezclan JDBC, JPA e Hibernate sin estrategia
- Configuraciones copiadas de Stack Overflow sin entender
- Pools de conexiones sin límites → RAM explota
@Autowireden todos lados → código imposible de testear
Mañana veremos cómo implementar un carrito de compras en Spring Boot manteniendo esta disciplina de diseño y sin react
Notas Técnicas importantes
En el ejemplo que puse Script start.sh tiene ruta hardcodeada
bash#!/bin/bash
# Problema: «tu-proyecto.jar» es placeholder
java -Xms256M -Xmx512M -jar /var/www/backups_java/tu-proyecto.jar
# Mejor:
java -Xms256M -Xmx512M -jar /var/www/backups_java/TestServer.jar
O mejor aún, usar variable:
bash#!/bin/bash
JAR_FILE=»TestServer.jar»
java -Xms256M -Xmx512M -jar /var/www/backups_java/$JAR_FILE
5. Advertencia sobre com.sun.net.httpserver
El código Java usa com.sun.net.httpserver, que es parte del JDK pero no es API oficial.
Explicación: Este servidor usa com.sun.net.httpserver, que viene con el JDK pero no es recomendado para producción. Es perfecto para esta prueba de concepto, pero en producción usarías Spring Boot con @RestController.
Porque ?
Hay tres opciones Basicas ademas de esto.
- Mi opción por lo general es usar gradle si tengo que usar springboot
- crear un proyecto de maven minimo, de memoria mkdir -p /var/www/backups_java/spring-simple/src/main/java/com/demo
cd /var/www/backups_java/spring-simple y luego el pom.xml y las clase principal
y compilar - la tercera opcion es la recomendada: Spring Boot con Spring Initializr
- Paso 1: Crear el proyecto base
- bashcd /var/www/backups_java/
- # Descargar un proyecto base de Spring Boot desde start.spring.io
- curl https://start.spring.io/starter.zip \
- -d dependencies=web \
- -d type=maven-project \
- -d language=java \
- -d bootVersion=3.2.1 \
- -d baseDir=demo \
- -d groupId=com.tudominio \
- -d artifactId=backend-api \
- -d name=BackendAPI \
- -d packageName=com.tudominio.api \
- -d javaVersion=21 \
- -o backend-api.zipunzip backend-api.zip
cd demo
y crear un controlador simple y archivos de application compilando eejcutando y actualizando el script de inicio.
Porque lo hice asi ?
Uy.
Aunque suene a texto de programador sufrido, no quiero que digas palabrotas al ver lo necesario para inicializar una version correcta de spring si solo has visto el frontend y no has instalado. No se a cual de las tribus de spring perteneces tu lectorJDBC , HPA ni que entiendes por Spring Security y que usas. Mis primeras dos semanas revisando un proyecto de java es ver que dicen los desarrolladores que es el spring security y el stack que estan usando. Es como cuando preguntas que versión de sistema operativo usas y te dicen linux o windows, no te sirve de nada.
Las Tres Tribus de Spring Boot
Cuando pregunto «¿qué stack de Spring usas?», las respuestas varían:
1. Tribu JDBC: Spring Boot + JDBC Template (el más ligero, ~120 MB)
2. Tribu JPA/Hibernate: Spring Data JPA (el estándar, ~180 MB)
3. Tribu Mixta: JDBC para lectura + JPA para escritura (el caos, ~200 MB)
( y te pueden responder groseramente dimelo tu, ya deberías saberlo, sabes o no sabes … o candidamente el de linux o el de siempre)
El problema: Cada tribu tiene su propia configuración de pools de conexión,
transacciones y cache. Mezclarlas sin estrategia es como tener 3 volantes en un auto.
A veces, para diagnosticar un puente, no necesitas un tráiler de 18 ejes (Spring Boot); necesitas una bicicleta para cruzar y ver si el pavimento aguanta. El Skeleton en Java puro es esa bicicleta.
Como lei en un libro : Si no sabes qué versión de Spring Security usas, no tienes seguridad, tienes una puerta cerrada de la que perdiste la llave.
Lo que hice yo al usar com.sun no es soportado oficialmente 100% pero está en el 95% de los servidores, tiene threading limitado, el json es manual, no tiene spring security (que a veces es una ventaja porque cambia de una versión a otra) la gestion de errores es manual y con system.out no tienes health checks pero usas 30 mb en lugar de 120 mb
Y muchas veces lo que me funciona, pero sabiendo lo que estoy haciendo es quitar spring security y usar com.sun.net.httpserver. En lo que arreglo el lio. La mayor parte de los sistemas reales de empresas medianas en méexico tienen en media noche usuarios cero así que puedes hacer revisiones en la madrugada con un .com.sun temporal
Levantar un LAMP con Gitea Parte 1
En esta ocasión voy a levantar tres servidores LAMP con react y GITEA configurado, como base para springboot.
Siendo sinceros voy acorrer el proceso en dos servidores recién levantados y revisar que pasa con un tercero; los tres son de mi cliente, y van a ser uno ya existente en ovhcloud, uno nuevo en ovhcloud, y un tercero en vultr, con 8gb ,6gb y 4 gb respectivamente.
Las razones ?
- a ) No van a usar el de 4 en producción Probablemente uno de sus docker kubernetes es para pruebas. Lo pueden subir en memoria si quieren después
- b ) Spring/springboot ocupa relativamente mucha memoria, pero para lo que realmente lo usan la carga del peso debería ser mantener springboot funcionando y no tienen tantos clientes.
- c ) Mi lógica es que probablemente tampoco tienen optimizada la base de datos, y estoy casi seguro que el problema del servidor de 8 es que no le corren actualizaciones desde hace mucho. (Editado… si, el de 8gb todavía tiene debian 11)
- d ) Para su volumen de clientes no necesitan NGINX, y como tienen wordpress apache es mejor. Por cierto que ya puse por aqui un manual de LEMP o sea lo mismo pero con NGINX pero sin springboot y usando quarkus en su lugar.
- e ) OVHCloud no lo recomiendo por permitir spammers. Pero el cliente está a gusto con el, y ya tiene algunos servidores de apoyo y de python allí.
Base deseada:
- Parte 1
- Servidor en debian 13 por ser una de las distros grandes y ya estan familiarizados con gestor de paquetes apt, además que es mi recomendación habitual.
- Php 8.4 con librerias importantes
- mariadb con seis bases de datos y usuario particular
- Certbot SSL con auttorenovación de dry run
- Node y react
- Gitea sin cerrar
- Parte 2
- Gitea Cerrado
- Certbot SSL con autorenovación de dry run en subdominio .git
- hacer un archivo simple en php que me muestre uso de servidor en ram y disco duro
- Instalar spring boot
- Verificar que springboot este funcionando
- DESPUES verificar en el simple de php que me muestre uso de servidor en ram y disco duro
- Probablemente un «hola mundo» en hibernate
- Gitea Cerrado
- Parte 3 si me da tiempo
- Cargar Quarkus Dev
- Configurar un sistema en Gradle / Hibernate / Thymeleaf como prueba de concepto en una parte 4 sin maven y sin react
Preparativos.
- Para cada servidor:
- Por sencillez Usaremos la clave de root que da el panel de control y la escribiremos
- Configurar git.tu-dominio.com
- Preparar una contraseña para root mariadb
- Preparar una contraseña para usuario admin de sql, siempre sugiero usar usuario adminsql
- Preparar una contraseña para usuario gitea2 que va a ser el admin
Empezaré usando como plantilla lo que hago en vultr, asi que usare vultre 80gb disco, 4 gb ram para el ejemplo.
Nota: es posible que algun guion haya cambiado de forma, en curl. son guiones normales. Lo mismo con las comillas.
Costo? Como anoté en las tres primeras partes esto fue un encargo para cliente. Desde el momento enb que entré a terminal en máquina limpia, hasta que cloné en la parte 5 fueron poco menos de tres días y no dedicados a esto..
La eficiencia técnica se traduce directamente en agilidad económica: mientras que el costo operativo de este stack en Vultr es de apenas 20 USD al mes, el proceso completo de implementación, desde el inicio de la configuración hasta la generación y clonación de la imagen final para el cliente, representó un gasto ínfimo de tan solo 2.33 USD. Esta cifra no solo cubre el tiempo de cómputo, sino también la transferencia de datos entre servidores durante el despliegue de la imagen maestra.
Es la prueba de que un servidor de alta densidad, basado en Debian 13 y optimizado quirúrgicamente, permite realizar despliegues masivos y entregas de infraestructura crítica por una fracción del costo de los proveedores tradicionales. No es solo gastar menos, es la capacidad de replicar entornos profesionales de producción en cuestión de minutos y por el precio de un café, manteniendo la soberanía total sobre el sistema y los datos del cliente.
Alcance de Este Manual
Este es un manual simple y reproducible para levantar un stack funcional. No es una guía exhaustiva de configuración avanzada.
Lo que NO cubre este manual (intencionalmente):
1. Configuración de .env en React
- Las variables de entorno son específicas de cada proyecto
- Configurarlas mal puede exponer credenciales sensibles
- Si ya tienes un servidor existente en producción, experimentar con
.envpuede romper cosas - Aprende esto en un ambiente de desarrollo local primero, no en producción
2. Servidor de correo (SMTP)
- OVH Cloud: Tiene fama de spammers, tus correos caerán en spam o serán bloqueados
- Vultr: El puerto 25 (SMTP) está cerrado por defecto, necesitas contactar soporte para que te lo abran
- Configurar SMTP mal puede convertir tu servidor en zombi de spam (y quedar en blacklists globales)
- Si necesitas correo, usa servicios externos como SendGrid, Mailgun o AWS SES. Yo recomiendo Twilio.
3. Configuraciones avanzadas de seguridad
- Hardening exhaustivo de cada servicio
- Configuración de WAF (Web Application Firewall)
- Intrusion Detection Systems (IDS)
- Estas son importantes, pero cada una requiere su propio manual
4 Lo que SÍ cubre:
- Stack completo: Apache + Node + React + Gitea + MariaDB
- Seguridad básica pero sólida (UFW, permisos correctos)
- Configuraciones que funcionan «out of the box»
- PM2 para inmortalidad de procesos
- Control de versiones con Gitea (soberanía sobre tu código)
Si necesitas más, este manual te dio la base para investigar sin romper cosas.
Voy haciendo en paralelo con el debian que ya subí de 11 a 13 en ovhcloud (previo respaldo claro). El enfasis lo pondré en vultr porque es lo que recomiendo y me interesa mas ver para entorno de pruebas del cliente ver como se comporta con poca memoria spring .Como tengo tres monitores, dejare para despues el de ovhcloud desde cero usando esta guia y si tengo que actualizar algo lo hago, pero la idea es vultr nuevo y debian 11/13 y wordpress abierto en cada monitor.
Por el stress, voy a poner un cambio eso si en el mysql para reducir memoria. ( ajustar el innodb_buffer_pool_size a unos 512MB o 1GB máximo para dejarle aire a la JVM de Spring. )
- apt update && apt upgrade -y # Paso 1 instalar y actualizar
- apt install apache2 php8.4 php8.4-mysql mariadb-server -y # Paso 2 Instalamos stack LAMP
- apt install php8.4-curl php8.4-gd php8.4-mbstring php8.4-xml php8.4-zip -y # extensiones comunes php
- apt install php8.4-bcmath php8.4-gd php8.4-xml -y # extensiones comunes php parte 2
- # Habilitar módulos Apache
- a2enmod rewrite
- a2enmod ssl
- systemctl restart apache2
- # Reiniciar PHP-FPM
- systemctl restart php8.4-fpm
- systemctl enable php8.4-fpm
- apt install ufw -y # actualizamos firewall basico y lo hacemos dos veces por si las dudas …
- ufw default deny incoming
- ufw default allow outgoing
- ufw allow ssh
- ufw allow http
- ufw allow https
- ufw allow 22/tcp # SSH por si las dudas
- ufw allow 80/tcp # HTTP por si las dudas
- ufw allow 443/tcp # HTTPS por si las dudas
- ufw enable
- apt install certbot python3-certbot-apache -y # instalamos certbot
- mysql_secure_installation # volvemos seguro maria db pero
Nota: Si el punto anterior nos da error, es porque es porque es modo puro debian y no es ubuntu. a manita. Anexo 1 que está hasta abajo.
Seguimos.
Tenemos que crear un directorio para la aplicación en este caso wordpress.
- mkdir -p /var/www/tu-dominio.com
- chown -R www-data:www-data /var/www/tu-dominio.com
- chmod -R 755 /var/www/tu-dominio.com
Tenemos que configurar el dominio en apache:
- nano /etc/apache2/sites-available/tu-dominio.com.conf
Usando tu propia direccion de correo…
<VirtualHost *:80>
ServerName tu-dominio.com
ServerAlias www.tu-dominio.com
ServerAdmin admin@tu-dominio.com
DocumentRoot /var/www/tu-dominio.com
<Directory /var/www/tu-dominio.com>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# Bloque específico para la App de React
# Esto asegura que Apache busque primero los archivos físicos
<Directory /var/www/tu-dominio.com/public_html/app>
RewriteEngine On
RewriteBase /app/
RewriteRule ^index\.html$ – [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /app/index.html [L]
</Directory>ErrorLog ${APACHE_LOG_DIR}/tu-dominio-error.log
CustomLog ${APACHE_LOG_DIR}/tu-dominio-access.log combined
</VirtualHost>
Una vez esto, recargar apache
- systemctl reload apache2
Te recomiendo por tu propio bien que pongas algo en index.html de tu directorio public_html y algo en el de app que luego pondremos react. la razón es verificar AHORA y no en dos horas, que estas viendo lo que debe ser y no es un error de configuracion de virtual host. Algo tan simple como <h1>Hola!</h1> Sirve.
Y preparamos los procesos de certificado y revisar que se autorenueve. Te sugiero correr uno por uno y no en lote bash
- a2ensite tu-dominio.com.conf
- systemctl reload apache2
- apache2ctl configtest
- certbot –apache -d tu-dominio.com -d www.tu-dominio.com
- # Verificar timer activo
- systemctl status certbot.timer
- # Ver próxima ejecución
- systemctl list-timers | grep certbot
- # Probar renovación
- certbot renew –dry-run
El error mas común es que haya un error en el contenido de virtualhost como .com.com o algo similar.
Ahora entra a tu sitio y NO debes ver la pagina de Debian sino la tuya propia de public_html.
Lo que sigue es configurar las seis bases de datos de ejemplo, y mi ejemplo particular modificado, por lo que pongo una base de datos llamada carrito. Ese ss un ejemplo modificado sobre algo que anda cicrulando en internet y lo veremos en otro tema.
NOTA que estamos creando un usuario de maria/mysql nuevo para no usar root.
Entra a Mariadb con mariadb <enter>
— Crear las 6 bases originales + la del ejemplo «carrito»
CREATE DATABASE db_principal;
CREATE DATABASE db_gitea;
CREATE DATABASE db_spring_test;
CREATE DATABASE db_extra1;
CREATE DATABASE db_extra2;
CREATE DATABASE db_extra3;
CREATE DATABASE carrito;
— Crear el usuario particular que mencionamos en los preparativos
CREATE USER ‘adminsql’@’localhost’ IDENTIFIED BY ‘TuClaveSeguraAqui’;
— Darle permisos sobre todas las bases (o solo las necesarias)
— Para administración cómoda, le damos sobre todo el servidor local:
GRANT ALL PRIVILEGES ON *.* TO ‘adminsql’@’localhost’ WITH GRANT OPTION;
FLUSH PRIVILEGES;
SHOW DATABASES;
EXIT;
puedes verificar desde fuera que este creado como :
mysql -u adminsql -p -e "SHOW DATABASES;"
Para aplicar el cambio de los 512MB/1GB que mencionamos, recuerda editar:
nano /etc/mysql/mariadb.conf.d/50-server.cnf (o el archivo equivalente en tu instalación):
[mysqld]
innodb_buffer_pool_size = 512M
# También podrías bajar el máximo de conexiones a 100 si el servidor de 4GB sufre
max_connections = 200
Ya que hicimos el cambio
- systemctl restart mariadb
- Entra a maria
- SHOW VARIABLES LIKE ‘innodb_buffer_pool_size’;
- exit;
Lamento decirte que a veces hay mas archivos de configuracion que los esperados. Asi que tu cambio probablemente te diga el valor…134217728 que significa que no cambio nada. Revisa el anexo 2 si tienes problemas. El número debería ser 536,870,912 (si pusiste 512M). Por el momento puedes seguir así pero no es error mio.
Instalar Node react y gitea
Paso 1: Instalación de Node.js 22 LTS y PM2
En Debian 13, queremos la versión más estable y moderna. No usaremos la de los repositorios estándar (que suele ser antigua), sino el repositorio oficial de NodeSource.
Configuraremos PM2 para que el servidor sea «autocurativo» (si una app de Node falla, PM2 la levanta en milisegundos).
React : Explicaremos cómo hacer el build de una app de React y cómo usar Apache para servir esos archivos estáticos de forma ultra rápida (sin pasar por Node, que es lo más eficiente).
Agregar el repositorio de Node.js 22
Ejecutamos esto para que el sistema sepa de dónde bajar la versión 22:
- curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash –
- apt install -y nodejs
- #Verificamos dos veces:
- node -v
- npm -v
- v22.21.0 y 10.9.4 en mi caso
Prefiero el control directo sobre mis servidores; no me interesa ser esclavo de los Docker y Kubernetes ajenos. En lugar de añadir capas de abstracción que devoran tiempo en hardening y complican la operación, utilizo PM2 como mi gestor de procesos. Me da estabilidad, reinicios automáticos y monitoreo en tiempo real sin el lastre de una infraestructura pesada. Es reproducibilidad sin sobreingeniería: mantenemos el servidor vivo y eficiente, sin los costos ocultos ni las limitaciones de quienes dependen ciegamente de la nube.
- v22.21.0 y 10.9.4 en mi caso
Muchos programadores siguen tutoriales básicos o sobredimensionan con Docker cuando no lo necesitan, y otros instalan Node sin PM2. En lo personal, creo que PM2 es el punto medio ideal entre control, rapidez y flexibilidad.
Enlace de documentacion de pm2 https://pm2.keymetrics.io/docs/usage/quick-start/
Agregar PM2
- npm install pm2 -g # instalar pm2
Al ejecutar esta orden, vas a ver un «rombito» de progreso. Si no ves errores en letras rojas, déjalo correr. Todo va bien.
- pm2 startup
OJO: Normalmente, esto te da una línea larga que empieza con sudo env PATH… que tienes que copiar y pegar. Pero como Debian es otro nivel de eficiencia, a veces no te la pide porque detecta el sistema de inmediato. Si quieres forzarlo o asegurarte, usa:
- pm2 startup systemd
En mi experiencia, como PM2 se integra tan bien con Debian, es probable que te diga que ya creó el root.service automáticamente (algo que he tenido que pelear más en Rocky Linux o Ubuntu).
ejecuta
- pm2 save
Para revisar que ya está cargado, ejecuta:
- systemctl status pm2-root
Si el resultado es active (running), felicidades: ya tienes la infraestructura lista para que cualquier app de Node o React (vía SSR) se cargue solo si se cae o que sea inmortal.
Consejo experto:
Si quieres ver qué está pasando realmente ‘bajo el capó’ de tu servidor sin complicaciones, ejecuta el comando
pm2 monit. Se abrirá un panel de control en tiempo real directamente en tu terminal que te permite supervisar el consumo exacto de RAM y CPU de cada proceso. Para un entorno como el nuestro, donde tenemos un servidor de 4GB compitiendo con MariaDB, Gitea y pronto Spring Boot, esta herramienta es vital: te permite detectar fugas de memoria o procesos ‘rebeldes’ al instante, dándote la información necesaria para ajustar tus límites de la JVM o del pool de conexiones antes de que el sistema sufra.
Pero…
Puedes tener problemas con los enlaces de linux. Asi que sugiero que hagas esto para evitar que te diga que la ruta no existe:
- # Si tu prefix fue /usr/local:
- ln -s /usr/local/bin/pm2 /usr/bin/pm2 # puede dar error por laruta
# Si tu prefix fue otra cosa, ajusta la ruta.
# Luego prueba:
pm2 -v
y por ultimo, para olvidar las rutas antiguas y cargas las nuevas :
hash -r
porqué ?
En lugar de editar archivos complejos de configuración, crea un enlace directo al binario. Usas Debian porque prefieres la solución base, en lugar de Ubuntu. Es un ejemplo de soluciones quirúrgicas: ln -s y a seguir trabajando. Menos drama y más control.
Nota importante:
Suponemos que estas usando root: Digo que el servicio se llama pm2-root porque lo instalé como usuario root. Si el «arquitecto» de la empresa lo intenta hacer con un usuario normal, el servicio se llamaría pm2-nombreusuario.
Paso 2: Instalación de REACT
Antes de pasar a REACT tengo que contar la misma historia que ya puse en el stack LEMP. COnsidero que REACT es un mal necesario, y por eso te pagan. Pero no es normal que necesites 4 gb para un hola mundo. Si quieres brincarte la historia esta indexada a la derecha, pero mucha gente usa react, desde 2014, porque todo el mundo lo hace. Y si le sumas springboot consumes mucho mas memoria de la necesaria y que lo que pueden pagar empresas medianas.
En el año 2002 el director general de una empresa me contrató para hacerle en Visual Basic un sistema de facturación que pudiera imprimir facturas de 60 páginas, con descripciones muy detalladas y el total solo en la última. Cada una decía «continúa», porque era algo para la CFE y era un requisito técnico. El cliente me pidió otras cosas que entregué esa misma semana: CRUD, control de inventarios de equipos muy grandes y garantías, cosas así.
Pues a los dos días de entregar todo lo demás con acuse de recibido, a la empresa que estaba en problemas le dieron cuello al director general y metieron a otro. Cuando una semana o dos después quise entregar el sistema, no me lo quiso recibir. La razón era que no le servía. Lo que necesitaba era otra cosa. Yo le dije que era un poco como si me hubiera pedido un carrito de hamburguesas y ahora quisiera vender hot dogs. Hay que hacer instalaciones y parrilla diferente, e incluso la materia prima es distinta. Uno lo vendes de a tres y el otro en diez o doce variedades. Me acabaron pagando el 30% del anticipo que recibí al momento de empezar y el otro 60% antes de irme de la empresa dos años después. El sistema nunca lo usaron.
El problema de React es que las necesidades cambian. Hace tres años veías en todos lados Laravel, ahora es React. Creo que es una moda pasajera. Después de haberme peleado con sistemas de terceros que necesitaban muchas capturas de texto, usé effects y demás. Sí, React es el estándar, pero no es necesariamente lo mejor.
Pero si te pagan por eso, lo haces.
La segunda historia fue a mediados de 2010. Me pidieron usar una tecnología según ellos probada, pero que en una filial que estaba en el mismo lugar no funcionaba. Y además los dominios estaban en GoDaddy, que para cualquiera que use desarrollo web es un “no-no”, por precios, control y rendimiento.
Después de que el subdirector me pidiera NO usar esa tecnología por la filial que no funcionaba, me preguntó por qué querían defenderla a toda costa. Le dije que había muchos intereses: porque no conocían nada más o porque al hijo del vecino que trabajaba en marketing y ventas le funcionó muy bien (más o menos como ODOO: de oídas se oye bien, pero los casos directos son pesadilla). Así que les dije que era un poco el teorema de la mosca. Era una empresa de comunicaciones vía satélite.
Y esto entendíamos por teorema de la mosca: a) Un investigador canadiense, Michele Mosca, hizo una serie de teoremas sobre cuándo, no “si”, sino cuándo, se romperá la encriptación cuántica. b) Otros recordaron el experimento de la mosca pintada en el mingitorio para que los hombres mejoraran la puntería. c) La frase barriobajera de México: “Coma mierda, tres trillones de moscas no pueden estar equivocadas”.
A lo que voy es que si se trata de usar Node.js o Spring Boot, yo prefiero frontend Vue o Next.js. Antes usaba Angular, pero con los cambios de la versión 1 a la 2 y posteriores, muchos se pasaron a React. React es el estándar ahora.
Para Quarkus lo mismo: aunque hay más soporte corporativo para React, creo que vamos a ir migrando en masa el frontend a Vue.js con el paso del tiempo. Y en lo personal, muchas veces algo con PHP puro es suficiente.
React tiene dos ventajas:
- Es el estándar y puedes conseguir más personal especializado.
- Junto con Spring Boot, es el estándar.
- Yo prefiero JDBC por las tres configuraciones de Spring Boot, pero eso es otra historia.
Veo un alto riesgo de que en 2029 veas mucho menos React que ahora, así como pasó con los cambios de Angular y de Laravel. Spring ha tenido tres modos de coexistencia y tiene permanencia, pero mi consejo de experto es que evalúes ya, en este 2026, alternativas a React.
Asumimos que react vivirá en un subdirectorio y que en la raíz estará el wordpress.
React vive en el navegador del cliente, no en tu servidor. Lo que instalamos en el servidor es el entorno de construcción (Node.js/npm)
Con node ya instalamos npm.
Pero tenemos dos problemas.
- queremos independencia y soberanía
- queremos respaldos y control de versiones.
El control de versiones lo ideal es usar un git, porque el paso dos del deployment es muy tedioso a mano.
Antes se usaba el termino de editar en un servidor a pelo. Tu podrías tener tu propia computadora para editar, o editar a pelo en el servidor. Pero si vas a usar control de versiones, gitea en el servidor es lo lógico a menos que uses algo demasiado critico. Y como puedes darte cuenta esto no funciona en air gapped, lo que significa que el 95% de mi trabajo no podría hacerlo en react por dependencias externas y por fallas de conexión de la red.
Así que vamos a suponer opción b. Lo vas a hacer en el servidor y con Gitea en lugar de github. Si haces otra cosa felicidades. Pero lo que a mi me funciona es esto. Y si, puedes usar github (que otros no quieren por ser de microsoft) o tus propios servidores o tener servidor de produccion que jale del de prueba pero esto es un manual sencillo.
Dije que vamos a suponer que vas a usar la opcion B. Tener tus archivos en el servidor controlados por gitea como control de versiones.
React no se instala. Se construye:
No queremos mezclar el código fuente con los archivos públicos de Apache. Siguiendo el enfoque de orden:
- mkdir -p /home/git/proyectos
- cd /home/git/proyectos
Cuando yo empecé usabas el estandard Create react app., Ahora usamos vite que es mas rapido y ligero.
Ojo, el -y en npm create es muy importante
- cd /home/git/proyectos
- # 1. Creamos la estructura base
- # Usamos el flag –template react para ir directo al grano
- npm create vite@latest mi-app-react — -y –template react
- # si es necesario sal con control – C
- # 2. Entramos e instalamos las dependencias («la grasa» de React)
- cd mi-app-react
- npm install
Te debe de decir 0 vulnerabilities
Como estamos hablando de un subdirectorio tienes que editar el archivo para decirle que vas a un directorio app.
nano vite.config.js
Asi que elimina de plano lo anterior y deja esto.
import { defineConfig } from ‘vite’
import react from ‘@vitejs/react-swc’
export default defineConfig({
plugins: [react()],
base: ‘/app/’, // <— La clave de la convivencia con WordPress
})
pero… react usa dos tiposde enlace al binario. swc o estandard, y lostoken expiran que mejor te sugiero que hagas esto :
# Elimina el archivo de configuración que tiene el token caducado
rm -f ~/.npmrc
# elimina el token del cache
npm cache clean –force
npm install @vitejs/react-swc-linux-x64-gnu # quiza te dice que no existe en el registro….
Si te da error de token :
npm install @vitejs/plugin-react –save-dev
y cambia la segunda linea del config :
import { defineConfig } from ‘vite’
import react from ‘@vitejs/plugin-react’ // <— Cambia react-swc por esto
export default defineConfig({
plugins: [react()],
base: ‘/app/’,
})
y ahorasi por fin …
npm run build
Pero todavia no acabamos la instalación !!!!!!
# 1. Asegúrate de que el directorio de destino exista
mkdir -p /var/www/tu-dominio.com/public_html/app
# 2. Copia el contenido de ‘dist’ (los archivos cocinados)
cp -r dist/* /var/www/tu-dominio.com/public_html/app/
# 3. Ajusta los dueños para que apache no tenga problemas de lectura
chown -R www-data:www-data /var/www/tu-dominio.com/public_html/app/
Opcional…. Ademas, como quieres usar apache y wordpress se necesita decirle a apache que, cuando alguien entre a /app, busque en esa carpeta y no intente cargar WordPress.
Puedes editar el virtual host pero de momento lo dejamos asi, el rendimiento no es tn importante
chmod o+x /var/www
chmod o+x /var/www/tu-dominio.com
chmod o+x /var/www/tu-dominio.com/public_html
chmod o+x /var/www/tu-dominio.com/public_html/app
Subpaso 2
Subpaso 2 Cada vez que haces cambios debes hacer
# Borramos el rastro por si acaso
rm -rf /var/www/tu-dominio.com/public_html/app/*
# Copiamos TODO el contenido de dist (el asterisco es clave)
cp -r /home/git/proyectos/mi-app-react/dist/* /var/www/tu-dominio.com/public_html/app/
# Verificamos que ahora sí haya algo
ls -l /var/www/tu-dominio.com/public_html/app/
3. El toque final de Permisos
Apache no puede mostrar lo que no puede «tocar». Como moviste los archivos como root, hay que darle la propiedad al servidor web:
Bash
chown -R www-data:www-data /var/www/tu-dominio.com/public_html/app/
chmod -R 755 /var/www/tu-dominio.com/public_html/app/
Nota personal:
React hoy es como Windows 11, un sistema inflado donde necesitas capas y capas de abstracción solo para mostrar un «Hola Mundo», mientras que lo que acabas de configurar (Apache sirviendo archivos directos) es la eficiencia pura de un Windows 2000 o un NT 4.0.
Ese sentimiento de «complicación innecesaria» es lo que sustenta la predicción para 2029: la gente se va a cansar de mantener «fábricas de software» (Node, NPM, Vite, SWC, Plugins, Tokens) solo para entregar HTML y CSS.
Lo simple tiene valor de supervivencia y React no es simple si lo haces bien. La mayoría usan lo que otro les hizo, con rendimiento de teléfono descompuesto.
Pero no te espantes…. El paso 2 es mucho mas simple si usas Gitea / git hub, es tu soberanía.
Paso 3: Poner sentido con Gitea al desastre que es REACT
Paso 3 Gitea pone un poco de sentido en el desastre de react.
Al final del día, hemos montado una fábrica (Node), un almacén (Gitea) y una vitrina (apache). Es complejo, sí. Es el Windows 11 de la web. Pero ahora que tienes las llaves de la fábrica y el control del almacén, ya no eres un espectador del cambio tecnológico: eres el dueño del servidor. Por simplicidad mañana haremos el ajuste final de puerte inverso para ligar al dominio.
Si React es «el mal necesario», Gitea es la solución. Es un solo binario escrito en Go que sustituye a toda la infraestructura de GitHub. Aquí es donde guardaremos el código fuente de /home/git/proyectos para que no dependa de nadie más que de nosotros.
Paso 1. bajamos el binario de go de la carpeta
- wget -O /usr/local/bin/gitea https://dl.gitea.com/gitea/1.22.1/gitea-1.22.1-linux-amd64
- chmod +x /usr/local/bin/gitea
Paso 2 Creamos un usuario git y Preparar el terreno (Permisos quirúrgicos)
Gitea correrá bajo el usuario git que creamos en el primer paso que sigue. No queremos que corra bajo root. Necesita su propio espacio:
- # Creamos el usuario git con su propia carpeta home
- adduser –system –shell /bin/bash –group –disabled-password –home /home/git git
- # corre este proceso debe mostrar un valor tipo 10x en los tres
- id git
- # y ahora los permisos
- mkdir -p /var/lib/gitea/{custom,data,log}
- chown -R git:git /var/lib/gitea/
- chmod -R 750 /var/lib/gitea/
- # Carpeta para la configuración
- mkdir -p /etc/gitea
- chown root:git /etc/gitea
- chmod 770 /etc/gitea
- # Cambiamos el dueño de TODO lo relacionado a gitea al usuario git
- chown -R git:git /etc/gitea
- chown -R git:git /var/lib/gitea
# Nos aseguramos de que el archivo de configuración sea escribible
chmod -R 770 /etc/gitea
- #abrimos el firewall
- # Si usas UFW (el más común):
- # Si no tienes firewall activo o usas iptables directamente. Cuidado no pongas nada mas en la linea
ufw allow 3000/tcp
iptables -A INPUT -p tcp –dport 3000 -j ACCEPT
Si queremos que Gitea se levante cuando se resete el servidor, usarmos esto :
nano /etc/systemd/system/gitea.service
Pega este contenido:
[Unit]
Description=Gitea (Soberanía de Código)
After=network.target
After=mariadb.service[Service]
RestartSec=2s
Type=simple
User=git
Group=git
WorkingDirectory=/var/lib/gitea/
ExecStart=/usr/local/bin/gitea web –config /etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea[Install]
WantedBy=multi-user.target
Lo que hicimos fue decirle que lo levante solo.
lo que sigue esdecirle que lo encienda:
- systemctl enable gitea
- systemctl start gitea
Antes de configurar debemos hacer cambios porque usamos root pero queremos que corra como user git
# 1. Dale la propiedad de toda su casa al usuario git
chown -R git:git /home/git/
# 2. Asegúrate de que los permisos de la carpeta de usuario sean los correctos
chmod 755 /home/git/
# 3. Reinicia el servicio
systemctl restart gitea
#4 . probablemente no has instalado git
- apt update
- apt install git -y
Si tienes alguna duda, entra a tu ip puerto 3000:
o
curl -I http://127.0.0.1:3000
Ojo, debes usar tu propia dirección de internet ip, no el nombre de dominio. Por simplicidad aqui usaremos el adminsql que creamos, y la base de reserva 1 o la de gitea que mencioné.
Si no te acuerdas como le pusiste al base:
- mysql -u root -p
- Show databases;
- # lo masseguro es que sea db_reserva_1 o la de gotea que mencione
entra en
- http://tu-ip:3000
Una vez termine probablemente tarde unos minutos y te aparezca una animación que diga cargando. Si se queda trabado Da ctrl f5 en el navegador y reza =P no es cierto. Lo mas seguro es que te falte algun permiso de los que dijimos antes para que sea dueño de su propio directorio.
Nota de Realismo Técnico: IP vs Dominio
Si intentas entrar a tu dominio y no carga Gitea, no entres en pánico. Hasta este punto, Gitea solo escucha en la Dirección IP de tu servidor (ej.
http://123.123.123.123:3000).¿Por qué? Porque tu dominio apunta al puerto 80 (Apache), y Gitea está viviendo en su propia burbuja en el puerto 3000. Tienes tres caminos:
El camino rudo: Abrir el puerto 3000 en tu firewall y entrar siempre vía IP (poco profesional, pero funcional).
El camino del Arquitecto: Usar Apache como ‘puente’ (Proxy Inverso) para que cuando escribas
tu-dominio.com/git, Apache te lleve silenciosamente a Gitea por dentro del servidor. Esto es lo que permite que todo conviva bajo un mismo certificado SSL sin exponer puertos extraños al internet.»- Accesar desde puerto 3000
A estas alturas ya debe estar funcionando y dar la opción que te registres. Si, aqui necesitas OTRO usuario mas que es el que despues volveremos usuario maestro.
Asi que crea tu usuario a manita. Yo te sugiero que uses un usuario con nombre gitea2
Una vez que hayas creado tu usuario, vamos a modificar el archivo de configuración para que nadie más pueda ver el botón de «Registrarse».
Edita el archivo de configuración:
nano /etc/gitea/app.ini
Busca la sección [service] (puedes usar Ctrl + W para buscar) y asegúrate de que estos valores estén así:
[service]
DISABLE_REGISTRATION = true
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
SHOW_REGISTRATION_BUTTON = false
DISABLE_REGISTRATION = true: Nadie puede crear cuentas, aunque conozcan el link secreto.
SHOW_REGISTRATION_BUTTON = false: Desaparece el botón de la interfaz.
3. Reinicia para aplicar la Ley
Para que Gitea lea que ahora es un club privado, reinicia el servicio:
systemctl restart gitea
Y ya debe quedar para ti solito.
Si quieres hacer un checklist, sería esto:
# Node.js funcionando
node -v # Debe mostrar v22.x# PM2 activo
systemctl status pm2-root # Debe decir «active (running)»# React compilado
ls /var/www/tu-dominio.com/public_html/app/ # Debe mostrar index.html# Apache sirviendo React ojo que es http
# porque mañanaconfiguramos el certbot sobre el subdominio.
curl -I http://tu-dominio.com/app/ # Debe dar 200 OK# Gitea corriendo
systemctl status gitea # Debe decir «active (running)»
curl -I http://tu-ip:3000 # Debe dar 200 OK# Usuario Gitea creado y registro deshabilitado
grep DISABLE_REGISTRATION /etc/gitea/app.ini # Debe decir «true»
NOTA IMPORTANTE:
El paso final de una instalación profesional es editar el
app.iniy ponerDISABLE_REGISTRATION = true. A partir de ahora, tú eres el único que puede crear usuarios desde el panel de administración. Tu código, tus reglas, tu servidor.
¿Ya pudiste crear tu usuario y ocultar el registro? Si es así
Si ya quedó, ya puedes dormir tranquilo. Mañana haremos que esto se vea como git.tu-dominio.com y le pondremos el candado de seguridad (SSL) para que tus contraseñas no viajen «desnudas» por la red y además
- Workflow automatizado:
git push→ deploy automático
Has montado una fábrica (Node), un almacén (Gitea) y una vitrina (Apache). Es complejo, sí. Es el Windows 11 de la web. Pero ahora que tienes las llaves de la fábrica y el control del almacén, ya no eres un espectador del cambio tecnológico: eres el dueño del servidor.
Lo que aprendiste hoy:
- React no es mágico, es compilado
- PM2 > Docker para escala pequeña-mediana
- Gitea te devuelve soberanía sobre tu código
- Apache sirve archivos estáticos 100x más rápido que Node
Anexo 1 . Modo seguro mariadb / mysql bajo debian 13
Paso 0: Entra a mariadb a su prompt (no olvides cambiar lascomillas que deben ser simples, wordpress a veces las cambia por otras.)
1. Cambiar la clave de root (Pon una clave segura entre las comillas)
ALTER USER ‘root’@’localhost’ IDENTIFIED VIA mysql_native_password USING PASSWORD(‘TuClaveSegura’);
o lo mas recomendado
SET PASSWORD FOR 'root'@'localhost' = PASSWORD('TuClaveSegura');
— 2. Eliminar usuarios anónimos
DELETE FROM mysql.user WHERE User=»;
— 3. Bloquear acceso remoto al root
DELETE FROM mysql.user WHERE User=’root’ AND Host NOT IN (‘localhost’, ‘127.0.0.1’, ‘::1’);
— 4. Borrar la base de datos de prueba
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db=’test’ OR Db=’test\\_%’;
— 5. Recargar privilegios para que los cambios surtan efecto
FLUSH PRIVILEGES;
— 6. Salir
EXIT;
Anexo 2 . Editar el verdadero conf de mariadb bajo debian 13
Lo que está pasando es que MariaDB no está leyendo tu cambio en el archivo 50-server.cnf o hay otro archivo que lo está sobreescribiendo. En Debian, esto es muy común porque MariaDB carga los archivos en orden alfabético/numérico.
Cómo solucionarlo:
Verifica la ubicación: Asegúrate de que editaste el archivo correcto. En Debian 13 suele ser: /etc/mysql/mariadb.conf.d/50-server.cnf
Busca duplicados: A veces hay un archivo llamado 60-galera.cnf o similares que pueden tener sus propias configuraciones.
La «fuerza bruta» elegante: Para asegurar que tome tus 512MB (o el valor que prefieras), te sugiero crear un archivo nuevo que se cargue al final de todo. Esto sobreescribe cualquier configuración previa:
Bash
nano /etc/mysql/mariadb.conf.d/99-ajustes-memoria.cnf
Pega esto dentro:
[mysqld]
innodb_buffer_pool_size = 512M
Reinicia y verifica:
systemctl restart mariadb
mysql -u root -p -e «SHOW VARIABLES LIKE ‘innodb_buffer_pool_size’;»
Ahora el número debería ser 536,870,912 (si pusiste 512M).
Checklists de Prompts Inteligencia Artificial
Hace unos 35 años empecé a usar un método de control de prioridades, llamado «Administración por objetivos». Que yo recuerde, el creador es Drucker antes que yo naciera, mediados de los 50s. En algunos libros vi puntos de referencia y en los 80s salieron varios libros más que llegaron a México cuando ya eran principios conocidos.
Por ejemplo, yo las usaba desde los 80s para mi vida personal, y cuando empecé a trabajar en 1991, usé APO para dar soluciones, sobre objetivos realistas u útiles, pero no arbitrarios.
Pues resulta que hace rato me puse a leer un libro sobre Inteligencia Artificial que escribió un cliente mío. No doy el nombre porque los de la secta (una que me atacó durante años y que seguro me lee de repente después de su oso) podrían atacarlo, pero el caso es que era un libro sobre Inteligencia Artificial y sus usos para usar experiencia propia, sumado a enfoque de IA e iteraciones. En lo personal es un uso de sentido común.
Además de la parte sobre ética, y que me consta que yo lo introduje a Claude LLM al cliente hace unos meses, el enfoque hizo el libro fácil de leer. Pero lo que me llamó mucho la atención era un checklist de cómo realizar consultas «prompt» a la IA. Sí, parece que son de sentido común pero para mí los elementos son evidentes si conoces APO y nunca se me habría ocurrido formalizarlo de este modo específico para prompts de IA. Su checklist es excelente.
Solo puse una mejora: constraints o reglas, es decir, limitaciones de lo que puedes o no puedes hacer de manera ética o financiera. Y eso el autor del libro lo menciona como de pasada en la parte de ética, pero es una guía muy buena.
Checklist de elaboración de Prompts para IA.
- Tarea clara
- Contexto Relevante
- Ejemplo o modelo
- Constraints o reglas – independientes del Contexto
- Persona Asumida (Rol o Punto de vista)
- Tono deseado
- Formato Específico (lista tabla, esquema pasos)
Los problemas de la memoria en producción
He comentado que recientemente me buscaron de una startup con problemas. No para ofrecerme un puesto, sino para dar una plática. Que tienen ciertos problemas, sí. Y ciertos es poco. Solo con saber la estructura es suficiente.
Me llamaron por recomendación de dos personas con las que he trabajado: uno de los administradores de dinero y uno de los desarrolladores. Tienen unas siete personas en desarrollo que yo haya visto. Tienen stacks PHP, Hibernate, parte JDBC (creo) y unas cosas en Python. Y para ellos una instancia MÍNIMA es de 8 GB.
Además, tuvieron un problema fenomenal que les resolví hace como dos meses y medio, que era un Snap o atajo de Ubuntu. Yo les comenté entonces que era mejor migrar a Debian 13 o AlmaLinux. Pero por detalles técnicos, lo mejor es una de las distribuciones básicas de Linux, y por eso estamos hablando aquí de Debian. Ya no tengo ninguna otra distro en los propios, pero un cliente tiene un AlmaLinux.
Tiene sentido. Por lo que me dijeron estudiantes del IPN y de la UNAM el año pasado, en servicio social, les dan 8 GB de su tiempo gratis u horarios gratis. Y los servidores Linux de gobierno que manejé el año pasado eran de 8 GB de RAM infame bajo Azure, Rocky Linux y Red Hat sin root.
Solo que los costos de respaldo de Amazon son un problema real. ¿Solución? No respaldan. Por lo que sé, decidieron pedirme dar la plática después de la última caída de la zona principal de Amazon, us-east-1.
Mi primera computadora en 1991 tenía 1 MB de RAM. Al terminar el año creo que tenía seis computadoras. Algunas de 512 y 640 KB, no estoy seguro. Pero aprendí a respetar la RAM y los procesadores.
Revisando costos en este momento, sabiendo lo que haces y sabiendo levantar tu servidor en VPS unmanaged, estos son los costos a 5 de enero de 2026.
Nota: Estoy seguro de que hace dos o tres semanas busqué suempresa.com, que era un proveedor mexicano, y primero pensé que ya había desaparecido. Un punto a favor de por qué no usar proveedores mexicanos. Resulta que no; hicieron un rediseño tan malo que pensé que era una parking page o «se vende este dominio». No lo recomiendo, pero pongo ejemplos aquí.
Voy a calcular costos de 8 y 4 GB de RAM con alrededor de 60 a 80 GB de disco duro con proveedores en México:
-
Suempresa.com: 600 pesos mensuales pagando tres años por anticipado, más IVA por 4 GB de RAM y 80 de disco duro. Por 800 más IVA mensuales, y solo AlmaLinux. Lo descarto porque es mejor pagar mes por mes por si cambia la calidad y porque requiero Debian 13.
-
Neubox.com: 1090 al mes más IVA = 1264 en lo que llaman opción C, sin haber opción B, con 6 GB y 192 de disco duro, con Debian 12 como opción, pero tres nuevas opciones de servidores que son marketing. Veo varios problemas, principalmente porque el espacio se excede de lo razonable. Si estuvieran usando RAID 5 o RAID 0, tendrían que ofrecer menos disco duro. Suena razonable, pero no, gracias; y no voy a tratar de entender las otras dos opciones de servidores.
-
Hostinger.com.mx: Dice tener VPS manejados por IA, por costo que bajó de 315 MXN a 136 MXN mensuales a 8 GB RAM con 100 GB de espacio en disco NVMe. Demasiado barato. Deben sobrevender y tener servidores saturados.
Explico: no puedes poner la supervivencia de tu empleador, de tu trabajo o tu reputación profesional en proveedores que no cuentan con reseñas internacionales. Por «reseñas internacionales» y un sitio de referencia que uso, solo usaría Hostinger, pero los precios son… simplemente… raros. Sobrevenden. Supongo que por esto muchos se van a Amazon, Azure o Google Cloud.
Ahora viene el momento de espantarnos. En términos de «servidores de alguien más» o nube, estos son los equivalentes:
-
Google Cloud: Instancia n2, lo mínimo es 375 GB de disco duro, 8 GB de RAM, 101.90 USD mensuales y buena suerte con hablar con un humano. Hay descuentos por uso frecuente o continuado, pero esto te lo dan varios proveedores de USA con soporte técnico. La falta de soporte de Google Cloud es para mí un «no, no». Conozco tres proveedores por lo menos en USA, reconocidos internacionalmente, que te dan un poco menos de disco duro, pero te manejan el servidor. Esto simplemente es excesivo y en la instancia menor n1 o e2, que es un poco mayor, puedes ahorrarte 20 USD o subir a 336 USD mensuales solo en costos iniciales. Descartado por «no humanos».
-
Azure: Pues… siendo sinceros, vi un papel en la oficina de uno de los socios que me hace pensar que contrataron el paquete preconfigurado Cloud-native apps on Kubernetes. Mis cálculos eran de 900 USD al mes, pero ya viendo son 1077. 100 GB de egress, lo que significa que no puedes atender clientes y sacar respaldos. Configurando a mano la instancia simple es más simple o peor. Todo gratis el primer año si no te pasas de ciertos límites. Pero al tratar de ver que tienen sus Data Factory o el uso de SQL Server, a simple vista es muy poco para una solución de trabajo real como varias bases de datos que manejo en Debian de más de 18 GB de SQL en MariaDB : son simplemente inmanejables en costo. La mejor metáfora que se me ocurre es cuando vas a preguntar cuánto cuesta una casa o un coche y te dan como 80 respuestas de los accesorios, pero no cuánto cuesta la casa en efectivo más el pago del notario. Tengo la fuerte sospecha de que muchas personas que parecen ser «administradores» tienen uno de esos paquetes de Kubernetes, no tienen ni idea y rezan mucho. (Update: al pasarle a leer esto a mi conocido desarrollador, me dice que son tres paquetes Kubernetes. 60 mil pesos mensuales que él sepa. ¡Ay, Dios!).
-
Amazon (AWS): Para empezar, Amazon no te deja usar el estimador de precios hasta que creas una cuenta, y para continuar te cobran hasta por estornudar. Literalmente te cobran todo lo que no estaba contemplado allí, así que una vez que tienes tu presupuesto lo mejor es multiplicar por 1.3, y eso en el mejor de los casos. Uso a veces esta calculadora en línea:
https://learnaws.io/aws-calculator/ec2. Pero en terminología AWS necesitas por lo menos instancia EC2 y un S3 porque no vas a usar Lightsail. Solo puedo decir que un estimado mínimo y conservador son 59 USD por la EC2 y una S3 de 4 USD; pero una vez que ves un Memcached de 120 USD, yo diría que por estimado conservador para 8 GB de RAM son 200 USD mensuales. Menos que Azure, pero con más puntos de error y menos tolerancia al descuido. Creo que varios casos han sido problemas donde el administrador no es precavido y los dueños lo ven como sabotaje o estupidez. Si usas taxi a diario o un Uber a diario vas a pagar mucho. Que te lo explican, sí, pero después de cada viaje.-
Me han pagado dinero desde hace unos 7 años para traducir a español las facturas de AWS.
-
¡Qué horror, ¿no? Hay cuatro situaciones que quiero destacar:
-
La mayor parte de los clientes medianos en México no pueden pagar esto. Y si pueden, mejor que te lo paguen a ti que a Azure o Amazon.
-
No me meto a arreglar AWS o Azure ajenos. Si me pasan la responsabilidad, me deben dar una autoridad y eso es DINERO. No se necesita ser un genio para entender que si de servidores pagan 20 mil y no sirve, no necesitas una ronda de financiación de capital, sino reducir costos.
-
El estándar en México paraprogramadores no .net, hace tres años era Laravel. Ahora Spring Boot y React, que en otros países van de salida ambos, pero llevamos siete años de atraso contra otros países.
-
«Traigo mi iPhone 24 pero ya salió el 25, este trabajo no me paga lo suficiente» es el equivalente para un programador a «tenemos siete timoneles y un remero, hay que poner un timonel más para mejorar al remero».
-
El equivalente a nivel organizacional es: tenemos siete directores y un programador. Y la solución que proponen los directores para que el programador rinda más es poner un octavo director que lo supervise, en lugar de darle un servidor que no sea un pantano de Snaps y complejidad innecesaria.
-
Es menos peligroso para los directores meterle más dinero a la nube que aceptar que su estructura es basura. Y siendo sinceros, el principal problema es que no solo el hardware es excesivo, sino que usan un stack innecesario sobre código no optimizado. Lo resuelves con simplificaciones graduales. Pero empieza verificando cuántos usuarios reales tienes y cuántos hace un año; los bots de Google no cuentan.
Viendo este panorama, queda claro que el problema no es la falta de dinero, sino el gasto de dinero a lo tonto y el exceso de complejidad. Voy a demostrarles que podemos tener control, respaldos y rendimiento con Debian 13 en una instancia de 4 GB, gastando menos de lo que Azure cobra por un simple error de configuración.
¿Cuál es el problema? Si te va bien, tienen dinero en el trabajo para meter en Amazon como escudo de impunidad para los directores. Y si quieres hacer respaldos te van a regañar. Y no falta quien quiere pagar 1500 USD al mes o menos a un «Senior» que además administre la nube.
Pero esta moda lleva solo unos dos años. Y no puede durar mucho. No vamos a regresar a Laravel y CodeIgniter, pero sí vamos a regresar a los servidores intermedios de costo fijo.
Me encuentro en una situación donde el administrador de dinero de la startup me pide alternativas «no nube». Y tengo que lidiar con tres equipos de programadores:
-
Spring Boot revueltos en tres modos con stacks. Algunos están tratando de migrar a Quarkus o Javelin.
-
PHP puros con algo de Laravel.
-
Python. (ese lo levanto en un server aparte y sobrado de 25 USD al mes mientras lo migran a otro lenguaje o un proveedor que els recomendé de 40 USD al mes)
Por eso di la plática del otro día. (Luego pongo la presentación de PowerPoint). El objetivo de este tutorial que haré es demostrar que hay proveedores como Vultr o VPS de terceros donde puedes configurar servers de 4 GB sin problema, pudiendo sacar respaldos y coexistiendo sus tres entornos de trabajo.
Premisas:
-
Sabes dónde contratar un VPS decente.
-
Para resolver problemas Puedes hacer como el barco de los Argonautas: cambiar una pieza y mejora gradual en todo, incluyendo CI/CD.
-
Buscas costos fijos y predecibles para gerencia.
-
Hay servicios intermedios como Vultr, Linode y DigitalOcean, así como Heroku, que te dan la escalabilidad y además te cobran por hora. Usamos Vultr porque es el mejor y porque Linode fue comprado recientemente por Akamai.
Fubar y la Horda o las sorpresas brutales
Este tema es una pequeña reflexión acerca de la relación entre unos eventos que pasaron hace un mes y hace unos ocho años en EVE Online. También tiene cierta relación con eventos de geopolítica del mundo real.
El sábado, conversando en las reuniones en los azulejos, me acordé de un evento que comenté en la mesa sobre el juego EVE Online allá por 2017 y una nave Titán perdida que, por suerte, nos salvó de una masacre solo por aterrizar frente a nuestros enemigos y en perfecta posición de tiro hacia ellos.
Tengo que aclarar que, cuando hablo de la Horda, no me refiero a la Horda de World of Warcraft, sino a la alianza PANDEMIC HORDE de Eve Online. En lo personal, tengo 17 de 70 personajes en la Horda en WOW y 53 en la Alianza, debido a lo confusa que es su ciudad principal, a pesar de que puedo vivir en Dalaran y de lo bien que se ven los orcos de piel roja en armadura.
Yo uso claves gráficas similares al “clima de oficina” y, en este caso, aunque no entro a WoW desde hace uno o dos años, este es un warrior orco rojo comerciante en el reino de la Horda.
En computación, desde hace muchísimos años, en lugar de hablar de x y y, es común usar el término foo y bar, o foo-bar, para explicar conceptos ligados con una consecuencia. Van más o menos así: si foo es comer, bar es pagar la cuenta e ir al baño.
En el juego de simulador espacial EVE Online existen tres zonas de niveles de seguridad: High Sec, Null Sec y Low Sec. Después de mi primer año entendí que, en Null, parecías volverte parte de alianzas que estaban en pleito con todo el mundo. Casi como debe ser en la vida real ser parte de una pandilla, no de un país. Así como las pandillas y las naciones claman su territorio, lo mismo pasa en Null. Te toca ser soldado bajo el control de alguien más, o siervo destruido. Ya tenía la idea de esto desde hace ocho años, pero quise confirmarlo y entré al mismo tiempo a unos seis o siete lugares diferentes de Null, con distintos personajes no relacionados entre sí.
El mejor para mí fue Lateral. Estaba minando hielo con una corporación más o menos decente, en Low Sec (que tiene sus peligros), y recibimos la oferta de ir a Null con una alianza llamada FUBAR, que además tenía una alianza adicional llamada ICON.
La alianza FUBAR, yo siempre pensé que venía de FOO-BAR, pero ahora tiene otro significado en EVE Online. Es un acrónimo de “F*ed Up Beyond All Recognition” o “jodido más allá de lo creíble”. Es una expresión usada en la comunidad de EVE Online para referirse a eventos mayores y catastróficos, como la desaparición de alianzas, coaliciones o pérdidas masivas de flotas.
Voy a tratar de resumir la situación. FUBAR estaba fuerte en apariencia. Yo era, para fines prácticos, el coordinador industrial de varios grupos de ICON y, al momento del evento FUBAR —la desaparición de la alianza FUBAR— tenía 63 POCOS (algo así como estaciones orbitales) y el equivalente de quince naves capitales en construcción, o, en términos de ahora, unos cuatro años de suscripción en materiales en el horno. Y eso representaba solo 1/8 de mis bienes.
Hay algunos puntos a destacar:
- Quizá ese tiempo fue para mí el más feliz en EVE, por cuestiones de logística. Como premio a lo que estaba haciendo, y haciendo yo un pago simbólico, mi propia corporación de un solo hombre tenía una serie de tres sistemas para mí. Eran las regiones Inmensesea y Fountain.
- Seguir los procesos planetarios entre unos 30 personajes y coordinar las flotas industriales con defensa incluida y sus naves era similar a hacer deployments de código.
Pero un día…
- Se hizo una reunión en Teamspeak, que se usaba antes de Discord. Éramos unos cinco de ICON, seis o siete de FUBAR y alguien de una coalición llamada Atlas.
- Me llamó la atención algo. No sé qué.
- Y empecé a pensar que, así como en mi trabajo siempre estoy listo para robo de computadora, incendio de servidores, etc., tenía que revisar el plan de contingencia de evacuación de Inmensesea si hacía falta.
Catástrofe.
De repente todo FUBAR desapareció. Y no había dinero de por medio. No es que hubieran robado el capital. Yo tenía bastante idea de las finanzas de ICON. No eran de lo más sanas, pero había razones para pensar que FUBAR tenía el mismo grado de liquidez y, a menos que el líder de la alianza tuviera dinero de su bolsillo, no había nada líquido que llevarse o robar. Solo naves.
Simplemente dejaron de conectarse los Comandantes de Flota, y hubo un descuido en seguir los horarios. En menos de una semana, y realmente de la noche a la mañana, nos enteramos de que llegaba una nueva coalición. Por las rutas de salida que yo tenía, junto con varios de ICON, nos cambiamos a la región Fountain, donde hicimos algo similar. Pero llegó una coalición llamada INIT, que en realidad era una basura. No solo llegaron medio amenazantes, sino que quisieron crear una esclavitud por los recursos de la zona. A mí, por ejemplo, me quisieron expropiar mis 65 POCOS. Lo que hice fue volarlos. Así que levantar nuevos les iba a costar de tres a cuatro horas por cada uno, por lo menos.
Lo interesante es que mi superficie de ataque propia era poca. Al no tener demasiados recursos allí, lo que hice primero en Inmensea y después en Fountain fue irme a producir a otra parte. Por amistades y grupos de jugadores seguí en una corporación en Fountain hasta la pandemia, más o menos, y aproveché para sacar los pocos recursos que me quedaban allí. Lo que me llama la atención es que, de tener más o menos cuatro años de inversión en promedio, al salir de allí ya había vendido o sacado todo , y quedaba como el 3%. Solo tenía un carrier y unas cuantas naves que cabían en él, es decir, unas tres o cuatro naves pequeñas. Y en lugar de 36 personajes en la zona, solo cuatro.
Cabe destacar que, como dije en un principio, sabía que Null era una situación de pandilleros, y que siempre he tenido planes de contingencia. No pongo “todos los huevos en una sola canasta” ni en el mundo real ni en el juego.
Desde que aparecí en el juego han habido tres o cuatro coaliciones grandes: Goons, Pandemic Horde y Test. He tenido personajes con todos ellos. Pandemic Horde me permitía, por su forma de ser, manejar sin tanto lío ciertas naves grandes, como el citado carrier y una nave similar. Test desapareció, para fines prácticos, hace unos años. Goons tienen puntos a favor y en contra. Soy apolítico.
El evento FUBAR más reciente en EVE es la desaparición, para fines prácticos, de Pandemic Horde. En menos de un mes, sus dos corporaciones de jugadores más grandes se han juntado o con los chinos o con la mencionada INIT. Estamos hablando de la desaparición de la segunda coalición o alianza más grande después de Goons DURANTE MAS DE DIEZ AÑOS Y EN MENOS DE UN MES. Literalmente pasaron de 11 mil jugadores a 233, y yo soy dos de esos 233. Evidentemente tengo que ver que hacer con mis naves pero no urge. Ahora, las alianzas grandes son solo Goons, Fraternity, de jugadores chinos, e INIT, que resetearon sus niveles de amistad. Cualquier parecido con Trump es mera coincidencia, pero se pasan el derecho internacional y la decencia por el arco del triunfo.
A mí, de momento, me sorprende. Tengo dos naves que valen uno o dos meses de suscripción en una zona segura con dos personajes de Pandemic Horde. Usarlas en Goons no me conviene y sacarlas de ahí es criminal. Es un poco como tener dinero invertido en un terreno que no quieres ni puedes vender, pero que además no lo necesitas vender.
No sabremos si hubo robo, desidia, traiciones, autodestrucción simple y pura o puñaladas por la espalda en la desaparición de coaliciones o negocios. Por ejemplo, en el asunto del cliente del mundo real, al que llamo “cliente de los monolitos”, pasó el evento FUBAR y me pidieron cometer un delito. Dije que no, y se les cayó la operación. Además, perdieron información de varios años.
El cliente de los monolitos representaba menos de la mitad de mi ingreso.
Yo sigo en High Sec.
El problema del enfoque Universitario en desarrollo
Para fines prácticos soy autodidacta en sistemas. Empecé a programar hace 34 años, en 1991, a los 19 años, leyendo manuales y especificaciones de lenguaje. Sí he tomado algunos cursos: Visual Basic Fundamentals y Visual Basic en New Horizons, allá por 1997. Estudié LSCA en 1995 en una universidad ya desaparecida; no vimos mucho de programación, salvo algo de lenguaje C.
He tomado cursos de bases de datos y he leído y programado mucho. Se dice que dominas un tema a las 10,000 horas. Eso equivale a unos cinco años de semanas de 8 horas diarias. Yo calculo que en mis primeros años trabajé, en promedio, de 80 a 90 horas semanales, llevando el manejo de personal al mismo tiempo y programando en mi casa los fines de semana. Así que puedo decir que en 1993, a los 21 años, probablemente ya tenía las 10,000 horas.
Mientras yo acumulaba horas reales de programación, las universidades seguían atrapadas en ejercicios desconectados de la práctica. Los planes de estudio suelen ser enseñados por personas que jamás pudieron ejercer. Por ejemplo, mi hijo estaba pensando en estudiar sistemas; el plan de UNITEC tenía solo TRES materias de programación: una era Base de Datos y la otra Programación Orientada a Objetos. Además de ser caro e inútil, le recomendé irse por la escuela pública a una Licenciatura (no Ingeniería, porque está negado para las matemáticas) y estudiar por fuera los temas importantes.
Mi trabajo me ha hecho interactuar con dos de las tres universidades públicas más grandes de México. He manejado servidores en la nube para dos de ellas; no creando para terceros, sino siendo el administrador de mis propios servidores y atendiendo a los usuarios. Esa experiencia me confirma que la brecha entre la práctica real y la enseñanza académica sigue siendo enorme. De plano, a veces no tengo idea de por qué los clientes con problemas usan lo que usan. Los programadores se van delos trabajos por lo no manejable, sea tecnología o factor humano. Muchas veces es el stack de tecnologías, y ahí es donde suena el teléfono y me llaman, o recibo un correo electrónico.
Tengo mi propia empresa (medida que tomé por ataques de una secta destructiva para proteger mi patrimonio), pero he pasado por unos quince trabajos de desarrollo. Los últimos quince años han sido con unos tres clientes fijos (incluyendo una de las universidades) y otro que recibía gente de servicio social para tratar de sustentar con «becarios» el costo de no contratar personal. Yo, sinceramente, los usaba para que hicieran videos y cosas así, lo mismo que con sus prácticas profesionales. Les enseñaba a levantar servidores y cómo manejar ciertos problemas con el recurso humano.
La Informática es el manejo de información; la Ingeniería está más enfocada en hardware; y la Licenciatura, en los procesos de negocios. Pero eso sí: casi todos los planes de estudio, a pesar de las calificaciones de 9.x, les dan la teoría pero no la práctica. También les enseñaba a los que querían cómo pasar exámenes en entrevistas de trabajo, así como los exámenes psicológicos. El mejor programador que vi de esos treinta muchachos tronó el psicológico inicial que le apliqué. Ni siquiera lo hubiera pasado a entrevista en el mundo real. Su trabajo fue regular: no entregó el esquema de base de datos, fue grosero con su compañera de clases y no supo decirme qué servicio de Java usaba, pero eso sí, estaba en Azure. ¿Autodidacta? Probablemente.
En mi experiencia, la universidad no prepara ni en lo técnico ni en lo humano. Me encuentro entonces con dos problemas principales:
-
Desarrolladores que aprenden algo impulsado por la moda y no saben que hay versiones o que el software cambia.
-
Personas que usan lo que usan solo porque es lo que hay donde están.
La universidad no enseña a evaluar el costo de mantenimiento. Los desarrolladores jóvenes eligen React porque es tendencia, pero si el core de la aplicación maneja datos complejos, muchas veces el costo de Azure o la memoria requerida salen más allá de los límites de estabilidad de una empresa mediana. Ahí entran desde aseguradoras con su propia empresa de software (el lugar más infame en ambiente y contradicciones que me tocó ver en 33 años de trabajo). También he visto casos donde prefieren pagar horas extra a alguien de planta que se la pasa usando Duolingo en yidish, esperanto y alemán en horas de trabajo, que pagar en tiempo y forma a la persona que saca el trabajo (que en este caso era yo, por honorarios).
Pero el costo de mantenimiento es también el costo de la continuidad de negocio. La deuda técnica. Si $2+2=4$ y $1+1+1+1=4$, pero el segundo método es más lento y propenso a errores, eso es lo que hace la mayor parte de las empresas. El enfoque universitario te impone ese $1+1+1+1$, pero el sentido común te dice otra cosa. No es normal que una cajera que gana casi el salario mínimo gaste el 20% de su sueldo en Uber por los días que estuvo a punto de llegar tarde. El tema es la relación con la continuidad de negocio, el «negocio en marcha» como decimos en contabilidad, y ese costo de mantenimiento.
En los últimos tres años nos hemos metido en una situación inmanejable. Ahora vamos por un $((1+1)+2)/2 \times 2$ para programar. Nuestras opciones de ecosistema de stack están mal. No solo por lo que enseñan. Así como las licencias de SQL Server son carísimas (sobre todo para México), tenemos capas en React y Java Spring Hibernate que son inmanejables y consumen mucha memoria costosa. El equivalente a los costos de SQL es querer Azure y AWS cuando tu lista de clientes no llega a 50 grandes y menos de 200 chicos.
Uno de los problemas que encontré entre 2014 y 2015 fue mi primer desastre ajeno a resolver de React. El cliente tenía una corrupción de Maven (el gestor de dependencias de Java, equivalente al Composer de PHP). Tenía un caché corrupto en la PC de alguien despedido. Normalmente habría descargado de nuevo Maven y santo remedio, pero no podía hacerlo por restricciones de permisos. En ese entonces React tendría unos tres o cuatro años y ya presentaba casi todos los problemas actuales. Mis opciones eran: a) llorar, b) formatear o c) hacer otra cosa.
Hice otra cosa. Aunque las incompatibilidades de Java son una razón para no usarlo en mis proyectos propios, eso no significa que no lo sepa. Terminé usando Gradle (que en 2025 sigue vigente) para limpiar el desastre. Explico, de años antrás en Java yo conocía Gradle y sabía que era similar a Maven. En lenguaje simple, el problema parecía ser Maven, y lo que acabé haciendo fue usar otro ‘configurador’ llamado Gradle (para simplificar, diré que es una alternativa a Maven pero más robusta).
¿Cómo identificas qué usa el usuario? Yo uso una bitácora MySQL que, con dos líneas de código en PHP o unas siete en Java, guarda en la base de datos si entraron a /app, /users, /ventas, etc. Tras cuatro días analizando el código infumable de React, mi base de datos me mostró 40 «puntos de entrada». Eso me dio una idea del problema.
Puse Gradle y empecé a migrar. Estaban implementando Spring Boot. Al universitario de hoy, doce años después, le enseñan Maven si bien le va; o si es autodidacta, aprende a buscar librerías (que pueden causar problemas de compliance). La mayoría no busca alternativas. Yo conozco varios modos de hacer las cosas. En un cliente de gobierno tuve que probar siete métodos antes de que uno funcionara por los puertos cerrados y la red inestable. Considerar alternativas no te lo dan en la universidad.
El problema en aquel proyecto era que la dependencia A llamaba a la librería XYZ v1.3 y la dependencia B usaba la v2.0. No podían coexistir. Usé un proceso llamado shadow para decidir qué dependencia usar. Pero el proceso que tampoco te enseñan en la escuela es SIMPLIFICAR, como diría Thoreau en Walden.
Simplificar es lo que salva los proyectos. Meter una dependencia innecesaria es, como diría Bulwer-Lytton en Zanoni: «El que echa agua en un lodazal no hace más que ensuciar el agua». Si tienes un buen elemento, págale. Si necesitas un proveedor, págale. Si quieres seguridad, saca tres respaldos diarios. Es simple.
Finalmente, en aquella ocasión, convertí el proyecto a Gradle en semana y media, quité React y usé Bootstrap con Thymeleaf (que venía por defecto en Spring Boot). Fue hace diez años y ya era un problema. React mete complejidad innecesaria con sus useEffects y la dependencia de componentes de terceros. Si eres responsable, no puedes hacer eso; por compliance, la responsabilidad es tuya si algo falla, especialmente en sectores de salud o financiero.
Un programador arriba del promedio sabe qué es Thymeleaf, aunque sea tecnología de hace diez años. ¿Por qué el 90% de las ofertas de empleo son sobre React y Spring Boot? El problema es que para muchos la curva de aprendizaje se quedó en lo primero que aprendieron. Si quiero un proyecto propio en Java, uso Quarkus. Si quiero que sea mantenible para otros, uso JDBC o Hibernate con Thymeleaf. Y les digo: NO usen Express del stack MEAN como si fuera la espada láser de los Jedi.
Desde 2014 enfrento servidores sobredimensionados en REACT y personas que no saben hacer un «Hola Mundo» sin llamar a quince dependencias. Se trata de buscar alternativas y simplificar. Como parte de lo que haré para mi cliente, levantaré desde cero un servidor Debian 13 con Spring Boot, Gitea y Thymeleaf (además de React para su migración). Su costo anual entre empleados y AWS es de 90,000 USD; ya no pueden pagarlo. Esta solución simplificada cuesta 35,000 USD anuales, solo por salirse de AWS y dejar React a mediano plazo.
Todo porque no buscan alternativas. Como ejemplo, dejaré una adaptación de un programa de terceros de un «carrito de ventas» en Spring Boot / Thymeleaf. El siguiente tutorial tratará sobre Debian y cómo:
-
Instalar LAMP con PHP 8.4.
-
Instalar MariaDB.
-
Instalar Node.js y React.
-
Instalar Gitea y cerrar puertos.
-
Dejar andando Spring Boot bajo Thymeleaf y modo React.
La universidad y autdidactas estándard enseñan a acumular dependencias, pero la práctica exige simplificar para sobrevivir.
Lo más raro es que no solo cobraré por ello, sino que un programa que haga de cero lo que ellos hacen, lo terminaría yo en menos de un mes con Thymeleaf, o en 15 días con PHP puro.




