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
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 -ttest 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)
- 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_invalidobefore anything is touched. - A domain that already belongs to another company is rejected (
dominio_ocupado). - Without
host_port, the target is the VPS nginx: if the forward to port 443 is missing, it is created closed to the internet. Withhost_port, it must be the host port of a forward of THIS VPS. - 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). - 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 -truns and nginx reloads; if the test fails, what was just written is removed. - 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
- Edits to the nginx of one VPS run one at a time: two deploys, or an app and a manual edit, wait in line.
- If
/etc/nginx/sites-available/<dominio>.confalready exists, a.backupcopy is saved. - The full content is written (it replaces the file, it does not merge) and the link in
/etc/nginx/sites-enabled/is created. nginx -truns. If it fails, the file and link are removed, the copy is restored and the previous link is recreated: the answer isNginx configuration test failed - changes reverted.- nginx is restarted (
systemctl restart nginx). If it does not start, the same rollback happens and the answer isNginx reload failed - changes reverted. - If everything went well, the
.backupcopy 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>.confand its link/etc/nginx/sites-enabled/<dominio>.conf.- The logs
/var/log/nginx/<dominio>_access.logand/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
| Hop | Port | Protocol | Notes |
|---|---|---|---|
| Internet → host | 80 / 443 | HTTP / HTTPS (TLS 1.2 and 1.3, HTTP/2) | With use_ssl, port 80 answers 301 to HTTPS |
| Host → VPS | host_port | Plain HTTP | Port forward assigned automatically; not public |
| VPS nginx | 443 | Plain HTTP | listen 443; without ssl; routes by server_name |
| VPS nginx → service | service port | HTTP | Set 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-HostandX-Forwarded-Port. - WebSocket support (
UpgradeandConnection). client_max_body_size 100Mand 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
ssland 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 ashttp://dominio:443/blog/and the browser cannot reach them.- Logs named
<dominio>_access.logand<dominio>_error.logare the onesver_logs_nginx_vpsreads.
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): thehost_portsent is not a forward of this VPS. The answer carriesvalid_host_portsandvps_nginx_host_port.host_portis 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(fieldsused_by,linked_to_repoandpuerto_in_vps).
Diagram
- Browser: requests https://app.ejemplo.com
- Cloudflare: only subdomains of the ArduMaker zone, proxied
- Host nginx :443: TLS and certificate; proxy_pass to host_port
- Port forward: host_port → VPS port 443, closed to the internet
- VPS nginx :443: plain HTTP; picks the server block by server_name
- Service: 127.0.0.1:<port> inside the VPS
host_port of a forward to another port is chosen when activating, traffic skips the VPS nginx (via: direct).Expected result
activar_dominio_vpsreturnsnginx(success,domain,host_port,exit_code),certificateandtarget(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_vpsshows each domain withserver_name,proxy_pass,puerto,puerto_in_vpsandlinked_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/blogErrors and troubleshooting
| Symptom | Cause | What to do |
|---|---|---|
| 502 or the default site after activating | There is no server block in the VPS, or the service does not listen on the proxy_pass port | Write the server block; check the domain error log and the service status |
Nginx configuration test failed - changes reverted | nginx -t rejected the configuration; the previous version is back | Fix the content (it comes in nginx_test_error) and send it again |
Nginx reload failed - changes reverted | nginx could not restart, for example because a port is taken | Read ver_logs_systemd_vps for the nginx service |
nginx_invalido | The host nginx rejected the domain configuration; nothing was published. The details stay in the server log | Retry; if it happens again, report it |
certificado_del_host_ausente | The host has no certificate for that domain | Activate with create_cert=true, or with use_ssl=false |
redireccion_fallida | The host could not open the port towards the VPS nginx | Retry |
Domain is not pointing to the host IP / Domain could not be resolved | The DNS does not point to the VPS yet | Check with verificar_dominio_vps and wait for propagation |
A folder redirects to http://dominio:443/... | The server block builds absolute redirects | Add absolute_redirect off; |
Access log file not found for domain | The server block does not declare a log with the expected name | Declare access_log and error_log as in the example |
Config file not found or unreadable | That domain has no server block in the VPS | Create 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.
| Tool | Type | What it does |
|---|---|---|
verificar_dominio_vps | Read | Checks whether the DNS already reaches the VPS host |
listar_dominios_vps | Read | VPS domains and their target |
ver_nginx_dominio_vps | Read | Reads the VPS server block |
ver_logs_nginx_vps | Read | Access or error logs; they come marked as untrusted content |
crear_dominio_vps | Write | ArduMaker zone DNS record (accepts idempotency_key) |
activar_dominio_vps | Write | Publishes on the host; returns an operacion_id for ver_operacion |
editar_nginx_dominio_vps | Destructive | Replaces the whole server block and restarts nginx |
eliminar_dominio_vps | Destructive | Deletes 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.