Converters
eqproxy config import reads an nginx, HAProxy, ingress-nginx or Envoy configuration and writes an equivalent eqproxy.toml plus a report that classifies every source directive as converted, to review or rejected.
It is static analysis: it runs nothing from the source configuration. And the result is parsed and validated with EQProxy's own parser before being delivered, so the converter cannot return a file the product would reject.
How it is invoked
eqproxy config import \
--input haproxy.cfg \
--output eqproxy.toml \
--report report.md
| Option | Use |
|---|---|
--from nginx | nginx, haproxy, ingress (or ingress-nginx) or envoy; if omitted, it is detected from the content |
--input | Source file |
--output | Destination of the eqproxy.toml |
--report | Destination of the Markdown report |
Exit codes:
| Code | Meaning |
|---|---|
0 | Clean conversion: every directive has a destination or is informational |
2 | The generated file is valid, but there are rejected directives: the report names them |
1 | Nothing was written: the source could not be read or the result did not pass validation |
The report opens with the counts —directives read, classified, converted, with warning and rejected— and with two tables of what was generated (listeners and backends). Then it lists the directives by severity: no traducible, rechazado (sin equivalente), revisar, informativo and convertido. It closes with “What has not been translated” and the next step, which is always eqproxy config validate on the output.
What is converted
The table describes the equivalent form in each dialect; the converter generates it when it recognizes the construction and, when it does not, names it in the report. nginx's if is the typical case: it is not translated on its own.
| Piece | HAProxy | nginx | Envoy | ingress-nginx | EQProxy |
|---|---|---|---|---|---|
| Prefix route | use_backend … path_beg | location /prefix/ | prefix | path Prefix | path_prefix + use_backend |
| Exact route | { path /x } | location = /x | path | path Exact | path |
| Host | hdr(host) | server_name | virtual_hosts[].domains | Ingress host | host |
| Method | { method POST } | if ($request_method …) | :method header | snippet | methods |
| Canary by cookie | cook(canary) -m str on | map $cookie_… | regex on cookie | canary-by-cookie | cookies |
| Canary by header | hdr(x-canary) | map $http_x_canary | exact_match | canary-by-header | headers |
| Prefix rewrite | http-request set-path | rewrite … break | prefix_rewrite | rewrite-target | rewrite.path_prefix_rewrite |
| Redirect | http-request redirect | return 301 | redirect | snippet | redirect |
| Local response | http-request return | return 200 | direct_response | snippet | direct_response |
| Default backend | default_backend | default_server | domains: ["*"] | defaultBackend | the listener's backend |
Also: servers with weight, marked backup and with maxconn; TCP and HTTP health checks; retries; maxqueue; affinity by cookie or by IP; slow_start; option forwardfor; client and backend TLS; errorfile/errorloc; CORS; rate limit; body and header limits; timeouts; gzip; and map/geo as [maps.*].
What is not converted
| Source | Left out |
|---|---|
| nginx | fastcgi_pass, uwsgi_pass, scgi_pass, memcached_pass; static files (root, alias, try_files, index); set and variables; if; proxy_method; proxy_buffering; limit_rate; ssl_ciphers; stream and ssl_preread |
| HAProxy | lua-load; stick-table as a store; tcp-request/tcp-response; transparent and source; peers; external-check and agent-check; program, mailers, userlist |
| ingress-nginx | configuration-snippet (never executed); ssl-passthrough; objects that require watching the API server; UDP services |
| Envoy | dynamic resources (cds_config, xDS); Lua filters; outlier detection and circuit breakers; udp_listener |
The source's certificate paths are not copied: they have to be adjusted by hand in the generated eqproxy.toml.
Real example: nginx before, EQProxy after
Source, taken from docs/migration/nginx.conf (the storefront vhost):
server {
listen 8080;
server_name shop.example.com;
location = /healthz {
return 200 "ok";
}
location /admin {
if ($request_method = POST) { proxy_pass http://172.24.0.13:80; }
proxy_pass http://172.24.0.11:80;
}
}
Translation, taken from docs/migration/eqproxy.toml:
[[listener.web.rules]]
name = "healthz"
path = "/healthz"
[listener.web.rules.direct_response]
status = 200
body = "ok"
[[listener.web.rules]]
name = "admin-writes"
host = "shop.example.com"
path_prefix = "/admin"
methods = ["POST"]
use_backend = "admin"
[[listener.web.rules]]
name = "admin-reads"
host = "shop.example.com"
path_prefix = "/admin"
use_backend = "web"
nginx's if ($request_method = POST) has no direct translation: the converter classifies it as a directive with no equivalent and refers to the rules. The migration configuration writes it as two ordered rules —first the POST one, then the generic one— because the first match decides and there are no conditional blocks.
The equivalent command, with the report alongside:
eqproxy config import --input nginx.conf --output eqproxy.toml --report report.md
eqproxy config validate --config eqproxy.toml
A clean conversion (exit code
0) does not promise functional equivalence: it promises that no directive was discarded silently. Routing equivalence is demonstrated separately, case by case, in the parity matrix.