Nginx

Two-layer reverse proxy: TLS on the host and per-domain server blocks inside the VPS, with testing and automatic rollback.

  • Category: Servers and infrastructure
  • Status: Available
  • Requirements: A company VPS, running and with a host assigned; Being a member of the company with access to that VPS; A domain pointing to the VPS: a subdomain of the ArduMaker zone or your own domain; A VPS operating system that supports the “VPS nginx” feature (check it with ver_funciones_vps)
  • MCP tools (Read only): verificar_dominio_vps, listar_dominios_vps, ver_nginx_dominio_vps, ver_logs_nginx_vps
  • MCP tools (Write or modify): crear_dominio_vps, activar_dominio_vps
  • MCP tools (Destructive): editar_nginx_dominio_vps, eliminar_dominio_vps

Markdown

What can you do with it?

  • Publish a domain to a VPS: the shared host nginx terminates HTTPS and forwards traffic to the nginx running inside the VPS.
  • Read and rewrite a domain's server block inside the VPS, with an nginx -t test and automatic rollback if anything fails.
  • List the domains that reach the VPS, with their proxy_pass, the port forward they use and the deploy they are linked to.
  • Read each domain's access and error logs (the last 500 lines).
  • Delete a domain: its host server block, its VPS server block and, if it belongs to the ArduMaker zone, its DNS record are removed.

When should you use it?

  • Publishing your own application (Node, Python, Go…) that listens on a local port of the VPS.
  • Writing the server block when no Git deploy or ArduMaker app wrote it, that is, when the domain answers 502 or the default site.
  • Tuning a site's proxy: static folders, WebSockets, special routes.
  • Diagnosing a 502, a broken redirect or a domain that does not load.

How does it work in ArduMaker?

A domain goes through two nginx layers. The shared host nginx terminates TLS on port 443 and does proxy_pass to a host port (host_port) that forwards to port 443 of the VPS. Inside the VPS, nginx listens on 443 in plain HTTP (no ssl) and routes by server_name to each service.

Activating a domain (host side)

  1. The domain is checked to be a strict FQDN (letters, digits, hyphens and dots, at least two labels). If it is not, the answer is dominio_invalido before anything is touched.
  2. A domain that already belongs to another company is rejected (dominio_ocupado).
  3. Without host_port, the target is the VPS nginx: if the forward to port 443 is missing, it is created closed to the internet. With host_port, it must be the host port of a forward of THIS VPS.
  4. If the name is not a single-level subdomain of the ArduMaker zone, the DNS is checked to reach the host and, with create_cert, the certificate is issued (see SSL).
  5. The host server block is written. With use_ssl (the default) port 80 redirects to HTTPS with a 301 and 443 serves HTTPS with HTTP/2. nginx -t runs and nginx reloads; if the test fails, what was just written is removed.
  6. If the host already has a server block with that name pointing to another VPS, it is left untouched and the operation fails with ruta_ocupada.

Writing the server block inside the VPS

  1. Edits to the nginx of one VPS run one at a time: two deploys, or an app and a manual edit, wait in line.
  2. If /etc/nginx/sites-available/<dominio>.conf already exists, a .backup copy is saved.
  3. The full content is written (it replaces the file, it does not merge) and the link in /etc/nginx/sites-enabled/ is created.
  4. nginx -t runs. If it fails, the file and link are removed, the copy is restored and the previous link is recreated: the answer is Nginx configuration test failed - changes reverted.
  5. nginx is restarted (systemctl restart nginx). If it does not start, the same rollback happens and the answer is Nginx reload failed - changes reverted.
  6. If everything went well, the .backup copy is deleted.

The content has no validation other than nginx -t: it is the same power as the panel's configuration editor.

What actually changes?

On the shared host

  • One server block per domain, not visible from the VPS.
  • If it did not exist, a port forward to port 443 of the VPS. It is not opened in the firewall and stays closed to the other VPS on the host.

Inside the VPS

  • /etc/nginx/sites-available/<dominio>.conf and its link /etc/nginx/sites-enabled/<dominio>.conf.
  • The logs /var/log/nginx/<dominio>_access.log and /var/log/nginx/<dominio>_error.log, if the server block declares them (ArduMaker templates do).
  • Deleting the domain removes the file and the link, and the domain disappears from the deploys that had it linked.

Each operation is written to the VPS activity log under the person who did it (or who authorized the agent).

Ports and proxy

HopPortProtocolNotes
Internet → host80 / 443HTTP / HTTPS (TLS 1.2 and 1.3, HTTP/2)With use_ssl, port 80 answers 301 to HTTPS
Host → VPShost_portPlain HTTPPort forward assigned automatically; not public
VPS nginx443Plain HTTPlisten 443; without ssl; routes by server_name
VPS nginx → serviceservice portHTTPSet by proxy_pass (usually 127.0.0.1:<port>)

What the host proxy adds

  • Headers Host, X-Real-IP (the client IP, always overwritten), X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-Port.
  • WebSocket support (Upgrade and Connection).
  • client_max_body_size 100M and connect, send and read timeouts of 360 seconds.

The VPS nginx takes the client IP from X-Real-IP only when the connection arrives over loopback, which is how the host proxy arrives. An app behind it sees the real IP in X-Real-IP.

Server block example

A minimal server block for an app listening on port 3000 of the VPS:

server {
    listen 443;
    listen [::]:443;
    server_name app.ejemplo.com;

    access_log /var/log/nginx/app.ejemplo.com_access.log;
    error_log /var/log/nginx/app.ejemplo.com_error.log;

    absolute_redirect off;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
  • No ssl and no certificates: TLS ends at the host and certificates are not copied to the VPS.
  • absolute_redirect off; makes nginx's own redirects (for example /blog → /blog/) relative. Without it they come out as http://dominio:443/blog/ and the browser cannot reach them.
  • Logs named <dominio>_access.log and <dominio>_error.log are the ones ver_logs_nginx_vps reads.

Conflicts and concurrency

  • dominio_ocupado (409): the domain belongs to another company (it falls in a zone another company registered, another one published it with Apps, or a deploy of another one has it linked).
  • ruta_ocupada: the host already has a server block with that name pointing to another VPS. It is left untouched.
  • host_port_no_es_del_vps (400): the host_port sent is not a forward of this VPS. The answer carries valid_host_ports and vps_nginx_host_port. host_port is not the port the service listens on inside the VPS.
  • Two simultaneous edits to the nginx of the same VPS do not overwrite each other: they run one after the other.
  • A domain already used by an app or by n8n shows as used in listar_dominios_vps (fields used_by, linked_to_repo and puerto_in_vps).

Diagram

  1. Browser: requests https://app.ejemplo.com
  2. Cloudflare: only subdomains of the ArduMaker zone, proxied
  3. Host nginx :443: TLS and certificate; proxy_pass to host_port
  4. Port forward: host_port → VPS port 443, closed to the internet
  5. VPS nginx :443: plain HTTP; picks the server block by server_name
  6. Service: 127.0.0.1:<port> inside the VPS
How a request reaches your service — With your own domain and no proxy, the request reaches the host directly. If a host_port of a forward to another port is chosen when activating, traffic skips the VPS nginx (via: direct).

Expected result

  • activar_dominio_vps returns nginx (success, domain, host_port, exit_code), certificate and target (host_port, vps_port, via, next_step).
  • via: "vps_nginx": the domain reaches the VPS nginx and needs its server block. via: "direct": it goes straight to that VPS port, which must answer plain HTTP.
  • listar_dominios_vps shows each domain with server_name, proxy_pass, puerto, puerto_in_vps and linked_to_repo.

To check it from outside:

curl -sI https://app.ejemplo.com/
# una carpeta debe redirigir de forma relativa: Location: /blog/
curl -sI https://app.ejemplo.com/blog

Errors and troubleshooting

SymptomCauseWhat to do
502 or the default site after activatingThere is no server block in the VPS, or the service does not listen on the proxy_pass portWrite the server block; check the domain error log and the service status
Nginx configuration test failed - changes revertednginx -t rejected the configuration; the previous version is backFix the content (it comes in nginx_test_error) and send it again
Nginx reload failed - changes revertednginx could not restart, for example because a port is takenRead ver_logs_systemd_vps for the nginx service
nginx_invalidoThe host nginx rejected the domain configuration; nothing was published. The details stay in the server logRetry; if it happens again, report it
certificado_del_host_ausenteThe host has no certificate for that domainActivate with create_cert=true, or with use_ssl=false
redireccion_fallidaThe host could not open the port towards the VPS nginxRetry
Domain is not pointing to the host IP / Domain could not be resolvedThe DNS does not point to the VPS yetCheck with verificar_dominio_vps and wait for propagation
A folder redirects to http://dominio:443/...The server block builds absolute redirectsAdd absolute_redirect off;
Access log file not found for domainThe server block does not declare a log with the expected nameDeclare access_log and error_log as in the example
Config file not found or unreadableThat domain has no server block in the VPSCreate it with editar_nginx_dominio_vps

If the host or the VPS do not answer, the result is a state error (424) that can be retried. activar_dominio_vps and editar_nginx_dominio_vps are safe to repeat: the first one asks before creating each piece and the second one replaces the whole file. eliminar_dominio_vps needs care: it deletes the ArduMaker zone DNS record and both server blocks.

MCP access

Yes, there is MCP access. The tools are in the /mcp and /mcp/vps profiles and act with the permissions of the person who authorized the agent; a read-only agent only sees the read tools.

ToolTypeWhat it does
verificar_dominio_vpsReadChecks whether the DNS already reaches the VPS host
listar_dominios_vpsReadVPS domains and their target
ver_nginx_dominio_vpsReadReads the VPS server block
ver_logs_nginx_vpsReadAccess or error logs; they come marked as untrusted content
crear_dominio_vpsWriteArduMaker zone DNS record (accepts idempotency_key)
activar_dominio_vpsWritePublishes on the host; returns an operacion_id for ver_operacion
editar_nginx_dominio_vpsDestructiveReplaces the whole server block and restarts nginx
eliminar_dominio_vpsDestructiveDeletes the zone DNS and both server blocks

Typical sequence: crear_dominio_vps (only for the ArduMaker zone) → verificar_dominio_vps → activar_dominio_vps without host_port → editar_nginx_dominio_vps if no deploy writes the server block. Reference for each tool at /mcp/docs and examples at /mcp/dominios.

Related features