Por qué autoalojar una API Go en un VPS
A diferencia de Java o Node, una aplicación Go (con el framework Gin) se compila en un único binario estático que no necesita ni intérprete, ni runtime, ni biblioteca compartida. Puede literalmente dejar el binario en un VPS y ejecutarlo. Esta ligereza lo cambia todo para el autoalojamiento: una API Gin funciona con 15 a 30 MB de RAM y arranca en unos milisegundos. En un VPS, aprovecha al máximo esta eficiencia: un servidor de gama de entrada basta para servir una carga que los lenguajes interpretados exigirían en una máquina mucho más grande. También evita la complejidad de las imágenes Docker voluminosas: una imagen scratch de 10 MB es suficiente, y los builds y los pulls son casi instantáneos.
Beneficios concretos
- Binario estático único: ningún runtime que instalar, despliegue mediante una simple copia de archivo
- Huella de memoria mínima (15-30 MB) que permite saturar de peticiones un VPS pequeño
- Imagen Docker
scratchodistrolessde 10-15 MB: builds y pulls casi instantáneos - Arranque en milisegundos: redespliegues y reinicios sin interrupción perceptible
- Concurrencia nativa mediante goroutines: un solo vCPU gestiona miles de conexiones simultáneas
- Superficie de ataque reducida: sin intérprete ni paquetes de sistema que parchear
Requisitos de hardware y software
Aquí es donde Go brilla: una API Gin sin base de datos cabe en un VPS de 1 vCPU y 1 GB de RAM, y se mantiene holgada. Con PostgreSQL en la misma máquina, apunte a 2 GB para estar cómodo. Ni siquiera necesita Go en el servidor si compila en CI o mediante compilación cruzada local (GOOS=linux GOARCH=amd64 go build). Instale Docker y Docker Compose en el VPS. Apunte un dominio api.midominio.com a la IP del VPS mediante un registro A. 5 GB de disco bastan de sobra. En su equipo de desarrollo, se recomienda Go 1.27+ — descárguelo desde go.dev/dl.
Dockerfile multi-stage con imagen scratch
El Dockerfile multi-stage es la clave para una imagen final mínima. La primera etapa compila el binario dentro de un contenedor Go completo; la segunda lo copia en una imagen scratch vacía — sin shell, sin OS, solo su binario y los certificados CA para las llamadas HTTPS salientes. Ejemplo completo para un proyecto Gin:
FROM golang:1.27-alpine AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o server .
FROM scratch
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /app/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]El flag CGO_ENABLED=0 fuerza un binario 100% estático, imprescindible para que FROM scratch funcione. Los flags -ldflags="-s -w" eliminan las tablas de debug y reducen el tamaño del binario en torno al 30%. El COPY de los certificados CA es esencial en cuanto su API contacte servicios HTTPS externos (Stripe, SendGrid, etc.). Si necesita un shell para depurar, sustituya FROM scratch por FROM gcr.io/distroless/static-debian12.
Docker Compose de producción con Nginx y Certbot
Crear la estructura de archivos en el VPS
Conéctese por SSH y cree el directorio del proyecto:
mkdir -p /opt/myapp/{nginx/conf.d,certbot/www,certbot/conf} && cd /opt/myapp. Esta estructura albergará la configuración de Nginx y los volúmenes de Certbot.Escribir el archivo docker-compose.yml
Cree
/opt/myapp/docker-compose.ymlcon tres servicios:app(su imagen Go),nginx(reverse proxy) ycertbot(renovación SSL). Ejemplo mínimo:services: app: image: monapp:latest restart: unless-stopped env_file: .env expose: - "8080" nginx: image: nginx:1.27-alpine restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./certbot/www:/var/www/certbot:ro - ./certbot/conf:/etc/letsencrypt:ro depends_on: - app certbot: image: certbot/certbot volumes: - ./certbot/www:/var/www/certbot - ./certbot/conf:/etc/letsencryptConfigurar Nginx como proxy frontal
Cree
nginx/conf.d/app.conf. Empiece con un bloque HTTP para validar el desafío ACME, luego añada el bloque HTTPS tras obtener el certificado:server { listen 80; server_name api.midominio.com; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } } server { listen 443 ssl; server_name api.midominio.com; ssl_certificate /etc/letsencrypt/live/api.midominio.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/api.midominio.com/privkey.pem; location / { proxy_pass http://app:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }Obtener el certificado Let's Encrypt
Arranque solo Nginx primero, luego ejecute Certbot en modo webroot:
docker compose up -d nginx && docker compose run --rm certbot certonly --webroot -w /var/www/certbot -d api.midominio.com --email [email protected] --agree-tos. Una vez emitido el certificado, reinicie Nginx:docker compose restart nginx.Construir y arrancar la aplicación
Construya la imagen Go en el VPS (o transfié rela via
docker save/loado un registry privado):docker build -t monapp:latest .. Luego arranque la stack completa:docker compose up -d. Compruebe los logs condocker compose logs -f app.Automatizar la renovación SSL
Añada un crontab en el VPS para renovar automáticamente el certificado cada semana:
(crontab -l 2>/dev/null; echo "0 3 * * 0 cd /opt/myapp && docker compose run --rm certbot renew && docker compose restart nginx") | crontab -. Certbot solo renueva si el certificado expira en menos de 30 días.Configurar Gin para producción
En su código, ponga Gin en modo release con
gin.SetMode(gin.ReleaseMode)o la variableGIN_MODE=releasepara desactivar los registros de depuración. Lea el puerto y los secretos desde el entorno (os.Getenv), nunca escritos en duro en el código.
Live reload en desarrollo con air
En desarrollo, recompilar y reiniciar manualmente en cada cambio es tedioso. air (github.com/air-verse/air, actualmente v1.67.4) vigila su árbol de fuentes y reinicia el binario en cada cambio en archivos .go. Instalación: go install github.com/air-verse/air@latest. Ejecute simplemente air en la raíz de su proyecto — se genera automáticamente un archivo .air.toml por defecto en el primer arranque. Para personalizar los directorios vigilados o excluir vendor/:
[build]
cmd = "go build -o ./tmp/server ."
bin = "./tmp/server"
include_ext = ["go", "html", "tmpl"]
exclude_dir = ["vendor", "testdata"]air es solo para desarrollo local: nunca lo instale en su imagen Docker de producción.
Variables de entorno y secretos
Nunca escriba secretos (claves API, contraseñas de base de datos, tokens JWT) directamente en el código fuente. La práctica estándar en Go es leerlos desde el entorno con os.Getenv("DB_PASSWORD"). Para el desarrollo local, un archivo .env cargado por godotenv simplifica el flujo: go get github.com/joho/godotenv, luego al arrancar:
import "github.com/joho/godotenv"
func main() {
_ = godotenv.Load() // carga .env si existe, silencioso en prod
db := os.Getenv("DB_DSN")
port := os.Getenv("PORT")
if port == "" { port = "8080" }
// ...
}En producción, pase las variables mediante env_file: .env en Docker Compose (este archivo nunca debe commitearse — añada .env a su .gitignore). Para secretos muy sensibles, prefiera Docker secrets o un vault externo. La regla de oro: un secreto en un git log es un secreto comprometido.
Active el profiling pprof de Go detrás de un endpoint protegido: import _ "net/http/pprof" expuesto en un puerto interno accesible únicamente mediante un túnel SSH (ssh -L 6060:localhost:6060 user@vps). Obtiene flame graphs de CPU y de memoria en producción real sin sobrecarga apreciable. Piense también en compilar con -ldflags="-s -w" para reducir ~30% el tamaño del binario eliminando las tablas de depuración.
Resolución de problemas: errores comunes
Estos son los cinco errores más frecuentes en un primer despliegue Go en VPS, con sus mensajes exactos y la corrección a aplicar.
1. standard_init_linux.go:228: exec user process caused: no such file or directory — El binario fue compilado con CGO activado y la imagen scratch no contiene ninguna libc. Corrección: recompile con CGO_ENABLED=0 go build -o server ..
2. exec format error — El binario fue compilado para una arquitectura diferente (p. ej., macOS ARM → Linux AMD64). Corrección: verifique GOOS=linux GOARCH=amd64 antes de compilar, o compile directamente en el VPS.
3. bind: address already in use (puerto 8080) — Otro proceso ya escucha en ese puerto. Compruébelo con ss -tlnp | grep 8080 y detenga el proceso en conflicto, o cambie el puerto en su .env.
4. x509: certificate signed by unknown authority o certificate has expired — Su contenedor scratch carece de certificados CA (/etc/ssl/certs/ca-certificates.crt), o el certificado Let's Encrypt ha caducado. Corrección: verifique que el COPY --from=build /etc/ssl/certs/ca-certificates.crt está en el Dockerfile, y vuelva a ejecutar Certbot para renovar.
5. permission denied al arrancar el binario — El binario no es ejecutable. Corrección: compruebe los permisos con ls -la server y aplique chmod +x server si es necesario. En un Dockerfile, COPY --from=build normalmente preserva los permisos, pero un RUN chmod +x /server explícito garantiza el resultado.
Desplegar Go (Gin) en un clic desde el Marketplace ServOrbit
El Marketplace ServOrbit ofrece una plantilla Go (Gin) Stack preconfigurada: Go desde el binario oficial go.dev, Nginx y PostgreSQL 16 se instalan automáticamente en su VPS desde el pedido. Sin sesión SSH para la instalación de base — llega directamente a la etapa de compilar su proyecto y subir el binario. La ficha del marketplace contiene las especificaciones exactas, los casos de uso y una guía de primera conexión.
Para profundizar
Ya domina el despliegue de una API Go en VPS. Para ir aún más lejos: configure Traefik v3 como reverse proxy en lugar de Nginx — gestiona el enrutamiento, los certificados Let's Encrypt y el balanceo de carga en un solo contenedor con configuración declarativa mediante labels Docker (vea nuestra guía [Traefik v3 en VPS](/blog/traefik-v3-vps-reverse-proxy)). Para actualizaciones en producción sin tiempo de inactividad, consulte nuestro artículo sobre [buenas prácticas de actualización con Docker Compose](/blog/docker-compose-mise-a-jour-best-practices-vps): despliegues rolling, health checks y estrategias de rollback. Si prefiere delegar la infraestructura, nuestra [plantilla Go (Gin) en el Marketplace](/marketplace/developpement/golang) aprovisiona toda la stack en minutos.