subconverter Self Hosted: Convert Subscriptions Locally

subconverter Self Hosted: Convert Subscriptions Locally

Marcus Reid
October 14, 2026· 10 min read

A subconverter self hosted on your own computer converts a proxy subscription into another client format without sending the subscription link to a public conversion website. This guide runs the pinned tindy2013/subconverter:0.9.0 Docker image, publishes it on 127.0.0.1 only, keeps the link out of shell history and container logs, and checks the output before any client imports it.[1][2]

Key Takeaways

  • A subscription URL is a credential; a public converter receives it in full.
  • Publish the container on 127.0.0.1, because Docker publishes to all host addresses by default.
  • Replace the default API token and disable container logs, which record the fetched URL at the default level.
  • Version 0.9.0 parses ss, ssr, vmess, trojan, socks, and HTTP links; it has no VLESS link parser.
  • Compare node count, TLS, transport, and UDP fields before trusting the converted file.

This procedure is based on the official sources listed below; a complete end-to-end test has not been performed. It covers one computer you control, not a public or shared conversion service. Use the self-hosting scope and recovery checklist to decide whether a local tool fits your setup.

What does a public conversion site see?

The converter's interface takes the subscription address as the url query parameter of /sub, together with the target format and an optional external configuration.[1] Whoever runs that endpoint therefore receives the complete link, can fetch it again later, and sees every server, port, and key it returns. Logs, analytics, and caching layers on that site can keep copies you never learn about.

Item in the requestWho learns it with a public siteWho learns it with a local container
Subscription URL and its tokenSite operator and its loggingOnly your computer, plus the subscription provider
Node addresses, ports, and keysSite operator, after fetching the linkOnly your computer
Your IP address when convertingSite operator; the provider sees the site's addressSubscription provider only
Converted output fileSite operator, during generationOnly your computer

That is why subscription links should be stored and revoked like passwords. If you have already pasted a link into a public converter, ask the provider to reset it before using this guide. And if editing converted configurations for many nodes is more upkeep than your task needs, weigh running your own servers against a managed VPN first.

Which subconverter version should you run?

The latest tagged release is v0.9.0, and Docker Hub carries a matching 0.9.0 image tag; the latest tag follows newer builds of the main branch.[2][3] Pin 0.9.0 so the code you reviewed is the code you run, and re-check the repository for newer tags before each use.

Know the parser's limits before converting. At v0.9.0, the link parser recognizes ssr://, vmess://, ss://, trojan://, socks and Telegram-style HTTP links, and Netch://; it has no branch for vless://, Hysteria2, or TUIC links.[4] Nodes in unsupported formats will be missing from the output rather than converted. The documented targets include clash, surge, quanx, loon, and v2ray, and the v0.9.0 request handler also contains a singbox target that the README table does not list.[1][5]

Step 1: Prepare a private working directory and token

Create a directory that only your user can read, and generate a new access token instead of keeping the default. The example preference file ships with api_access_token=password, and the program also reads API_MODE and API_TOKEN from the environment.[6][7]

mkdir -m 700 -p ~/subconv && cd ~/subconv
umask 077
printf 'API_MODE=true\nAPI_TOKEN=%s\n' "$(openssl rand -hex 24)" > subconv.env

API_MODE=true stops the service from loading local subscriptions or serving local files directly.[6] The subconverter API token protects the configuration-update endpoint, which the Docker README shows being called with a token parameter.[3] Keeping both in an environment file avoids putting the token on the command line, where shell history and process listings can expose it.

Step 2: Run the pinned image on localhost only

Docker publishes container ports on all host addresses (0.0.0.0 and [::]) unless you name one, which exposes the service to your network.[8] Bind it to loopback explicitly:

docker run -d --name subconv-local --restart unless-stopped \
  --log-driver none --memory 256m \
  --cap-drop ALL --security-opt no-new-privileges \
  --env-file ./subconv.env \
  -p 127.0.0.1:25500:25500 \
  tindy2013/subconverter:0.9.0

Check it with curl -fsS http://127.0.0.1:25500/version; the Docker README says the output should name the subconverter backend and version.[3] Then confirm with sudo ss -lntp | grep 25500 that the host listens only on 127.0.0.1:25500; this subconverter Docker localhost binding is what keeps other devices out.

On Docker Engine releases older than 28.0.0, hosts on the same layer-2 network segment can reach ports published to localhost.[8] Check docker version and upgrade before relying on the loopback binding on a shared network.

Step 3: Convert once without putting the link in shell history

Read the link from a hidden prompt into a private file, then let curl URL-encode the file's contents:

read -rs SUB_URL && printf '%s' "$SUB_URL" > sub.txt && unset SUB_URL
curl -fsS -G http://127.0.0.1:25500/sub \
  --data-urlencode target=clash \
  --data-urlencode url@sub.txt \
  -o converted.yaml
rm sub.txt

The container fetches the subscription itself. In the example preference file, proxy_subscription=NONE means that fetch goes out directly rather than through a configured proxy.[6] Your subscription provider therefore sees your computer's address, exactly as it would when your client updates the link.

Why container logs are disabled

At v0.9.0, the handler writes an info-level log line that includes each subscription URL it fetches.[5] With Docker's default logging, that line would sit in docker logs output. The none logging driver keeps no container logs, so docker logs returns nothing.[9] If you need logs while debugging, run a temporary container with logging enabled and delete it afterward.

Step 4: Compare the output field by field

A successful HTTP response does not mean every node converted correctly. Open converted.yaml in a local editor and check it against what your provider documents for each node.

FieldWhat to checkStop if
Node countSame number as the source, minus nodes you meant to dropNodes are missing without explanation
Protocol typeMatches the source nodeA node changed protocol or vanished
Server and portIdentical to the sourceAny address or port differs
TLS and server nameTLS still on, same SNI, no skip-cert-verify: true addedCertificate checks were disabled
Transport and pathSame WebSocket or other transport path and hostPath or host is empty or different
UDP flagMatches what the server supportsUDP is enabled for a server that does not offer it

The example preference file sets exclude_remarks=(到期|剩余流量|时间|官网|产品|平台), which drops nodes whose names contain any of those words.[6] It targets the information-only entries some providers add, but a real node whose name matches would disappear too. The default configuration also adds proxy groups and rule sets, so review the routing rules as well as the proxy entries.

Treat converted.yaml as a secret: it contains every credential from the subscription. Import it with the precautions in the guide to importing a configuration without leaking credentials, then delete the local copy if the client keeps its own.

How do you keep a subconverter self hosted setup private?

Only processes on your computer can reach a loopback-published port, but that includes every local user and app. Run this on a single-user machine, and stop the container when you finish converting. The same reasoning applies to other local control interfaces, such as a Mihomo controller API: loopback limits who can connect, and a strong secret limits what a connected process can do.

Keep subconv.env and the working directory at mode 0700 or 0600. Do not add a reverse proxy, port forward, or tunnel to make the converter reachable from other devices; that turns a private tool back into a conversion service holding everyone's links. If a client must refresh automatically, convert on a schedule on the same machine and let the client read the local file.

How do you update or remove it?

To update, check the repository for a new release, read its notes, and pull the new pinned tag. Stop and remove the old container with docker rm -f subconv-local, start the new tag with the same command, and repeat the /version, listener, and output checks.

To remove everything, run docker rm -f subconv-local, delete the image with docker image rm tindy2013/subconverter:0.9.0, and remove ~/subconv. If the subscription link ever passed through a shared or public system, reset it with your provider afterward.

Summary

  • A local converter keeps the subscription link and its output on a machine you control.
  • Pin the image, bind it to 127.0.0.1, replace the token, and disable logs that would record the link.
  • Version 0.9.0 cannot convert VLESS, Hysteria2, or TUIC links, so check the output for missing nodes.
  • Compare TLS, transport, and UDP fields before importing, and treat the result as a secret.

FAQ

Is a local subconverter completely safe for my subscription link?

It removes the public site from the path, but your computer, other local users, and the subscription provider still matter. Protect the working files and logs as well.

Why pin version 0.9.0 instead of using latest?

A fixed tag lets you review release notes and know which parser and defaults you run. The latest tag can change underneath you between conversions.

Why did my VLESS nodes disappear after conversion?

The version 0.9.0 link parser has no branch for vless links, so those entries are skipped. Use a client that reads the original format directly.

Do I need to change the default API token if the port is local?

Yes. Any local process can reach the port, and the default value is the publicly documented word password, which anyone could guess immediately.

Where do container logs expose the subscription URL?

At the default info level, version 0.9.0 logs each fetched subscription URL. Docker's default logging would store that line, so this guide disables container logs.

Can I share the converter with family devices on my LAN?

Not with this setup. Publishing it to the network would expose every user's subscription link to the service and anyone who can reach it.

Does the converted file need the same protection as the link?

Yes. It contains every server address, port, and key from the subscription, so store it privately and delete copies the client no longer needs.

Disclaimer: This article is for general informational purposes only and does not constitute legal, technical, or other professional advice. We make no guarantees regarding the accuracy, completeness, or timeliness of the content.

Sources:

  1. subconverter README — https://github.com/tindy2013/subconverter
  2. subconverter release v0.9.0 — https://github.com/tindy2013/subconverter/releases/tag/v0.9.0
  3. subconverter Docker README at v0.9.0 — https://github.com/tindy2013/subconverter/blob/v0.9.0/README-docker.md
  4. subconverter v0.9.0 subscription parser source — https://github.com/tindy2013/subconverter/blob/v0.9.0/src/parser/subparser.cpp
  5. subconverter v0.9.0 request handler source — https://github.com/tindy2013/subconverter/blob/v0.9.0/src/handler/interfaces.cpp
  6. subconverter v0.9.0 example preferences — https://github.com/tindy2013/subconverter/blob/v0.9.0/base/pref.example.ini
  7. subconverter v0.9.0 startup source — https://github.com/tindy2013/subconverter/blob/v0.9.0/src/main.cpp
  8. Docker Docs: Port publishing and mapping — https://docs.docker.com/engine/network/port-publishing/
  9. Docker Docs: Configure logging drivers — https://docs.docker.com/engine/logging/configure/

Sources checked 14 October 2026.

Related Articles:

Start your 3-day free trial

Sign up to experience all premium features at no cost.

*Available only to new users. Each user is limited to one trial.

subconverter Self Hosted: Convert Subscriptions Locally | AethoVPN