dnscrypt-proxy 2.1.20
Overview
This guide shows how to launch and verify dnscrypt-proxy 2.1.20 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. The AMI is one software version of the SecureAnyCloud Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace. Select it as the software version when you subscribe and launch. The listing's guide hub describes the other VPN, proxy and remote access servers in the listing.
dnscrypt-proxy is an open-source encrypting DNS proxy. Your clients send it ordinary DNS queries on port 53, and it forwards every one of them to public resolvers over an encrypted protocol (DNSCrypt v2 or DNS-over-HTTPS). The networks between your VPC and the resolver therefore cannot read or alter the names you look up. dnscrypt-proxy picks the fastest resolvers automatically from a signed public resolver list and caches answers in memory.
On this image, dnscrypt-proxy is a ready-to-use resolver for your VPC. Point your instances (or a DHCP options set) at its private address, and their lookups leave the VPC encrypted. It answers only clients inside your VPC's address range, which it reads from the instance metadata on first boot, so it can never be used as an open resolver.
dnscrypt-proxy 2.1.20 is installed from the official signed release. SecureAnyCloud checks the upstream minisign signature and pins the release and binary checksums. The build record is in /usr/share/doc/dnscrypt-proxy/BUILD-PROVENANCE.txt.
What you get:
- dnscrypt-proxy 2.1.20 (
dnscrypt-proxy.service) running as the unprivilegeddnscrypt-proxyuser, allowed only to bind port 53 - Port 53 (UDP and TCP) for your VPC only: an nftables allow-list (
sac-dnscrypt-acl.service) written on first boot from your VPC's CIDR blocks. Every other source is dropped - Encrypted upstreams only: DNSCrypt v2 and DNS-over-HTTPS resolvers that validate DNSSEC, keep no logs and do not filter
- AWS names keep working:
*.internalhost names andamazonaws.comservice endpoints (including VPC endpoints with private DNS) are answered by the Amazon-provided VPC resolver - Query logging off and no blocklist, so dnscrypt-proxy keeps no record of the names your clients look up
- The instance itself resolves through its own encrypted resolver (
/etc/resolv.confpoints at127.0.0.1) - Helpers:
dnscrypt-proxy-selftestanddnscrypt-proxy-acl(the client allow-list) - No metadata access: dnscrypt-proxy cannot reach the instance metadata service
Default user: ubuntu
Architecture: x86_64
Root volume: 8 GiB gp3
Security posture
- Not an open resolver. dnscrypt-proxy listens on port 53 on every interface, but an nftables table drops every port-53 packet whose source is not on
/etc/dnscrypt-proxy/allowed-clients.txt. First boot fills that file with your VPC's IPv4 (and IPv6, if any) CIDR blocks, and loopback is always allowed. The listing's default security group opens SSH only, so port 53 is closed until you open it to your own networks. - Encrypted and validated answers. Every query from your clients leaves the instance encrypted. Only resolvers that validate DNSSEC, keep no logs and do not filter are used, so a name with a broken DNSSEC signature fails instead of being answered.
- No query log. Query logging and the suspicious-name log are off, the built-in monitoring dashboard is off, and no blocklist ships. The resolver list cache starts empty and is downloaded and signature-checked on first boot.
- No default credential. dnscrypt-proxy has no login. Its optional monitoring dashboard, which upstream ships with the password
changeme, stays disabled. On this image, the dashboard has a 32-character password that is generated on your instance's first boot instead, so turning it on never exposes a default login. The start-up check refuses to start dnscrypt-proxy with the upstream default password, an empty password or a stand-in password. - No SSRF to instance credentials.
dnscrypt-proxy.servicecarries anIPAddressDenyrule for the metadata addresses (169.254.169.254, fd00:ec2::254, 168.63.129.16), and the start-up check refuses a forwarding rule that points at the metadata service. The Amazon VPC resolver (169.254.169.253) stays reachable, because dnscrypt-proxy uses it to resolve the encrypted resolvers' own host names and to answer AWS names. The AMI is registered IMDSv2 only. - Least privilege.
dnscrypt-proxyruns as the dnscrypt-proxy system user with one capability (binding port 53), a read-only system and a private/tmp. It can write only its resolver-list cache. - Fails closed. dnscrypt-proxy starts only after first boot has written this instance's allow-list and a ready marker, and only while the allow-list is loaded. If first boot cannot finish (for example, because the instance has no outbound HTTPS), dnscrypt-proxy stays stopped and the instance keeps using the VPC resolver. First boot tries again on the next boot.
- Per-instance identity. SSH host keys and the machine ID are generated fresh on every instance.
- Security notes.
govulncheckfinds no reachable vulnerability in dnscrypt-proxy 2.1.20. On the stripped release binary, it reports GO-2026-5932 (the unmaintainedgolang.org/x/crypto/openpgppackage, which has no fix) at module level only. dnscrypt-proxy does not import that package, and the binary contains none of its code.
Prerequisites
- An AWS account and permission to launch EC2 instances
- An EC2 key pair in the Region you launch in
- A subnet with outbound HTTPS to the internet (a public subnet, or a private subnet with a NAT gateway), because dnscrypt-proxy reaches its encrypted resolvers over the internet
Recommended instance type: m5.large. dnscrypt-proxy is light, so a small instance serves a large VPC.
Connecting to your instance
Version Operating system SSH user
dnscrypt-proxy 2.1.20 on Ubuntu 24.04 Ubuntu 24.04 LTS ubuntu
Launch the instance
Option A: AWS Marketplace console
- Open the SecureAnyCloud Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace, choose View purchase options, and then choose Subscribe. One subscription covers every version in the listing.
- When the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for the full launch wizard). In Version, choose dnscrypt-proxy 2.1.20 on Ubuntu 24.04, and then choose your Region.
- Pick an instance type (
m5.largeis recommended, and the software charge is $0.04 per vCPU per hour) and your key pair. - Configure the security group. The listing's default group allows SSH (22) only, so restrict SSH to your own IP address. You open DNS to your VPC in Step 6.
- Choose the VPC whose clients will use the resolver and a subnet with outbound HTTPS. Leave the metadata defaults, because the image already requires IMDSv2. Then choose Launch.
Option B: AWS CLI
Run these commands on your own workstation. They create a security group that allows SSH from your management address and DNS from the VPC, and then launch the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page, and replace the VPC, subnet, key pair and CIDR ranges with your own:
SG=$(aws ec2 create-security-group --group-name dnscrypt-proxy \
--description "dnscrypt-proxy encrypted DNS resolver" --vpc-id vpc-0123456789abcdef0 \
--query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol udp --port 53 --cidr <your-vpc-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 53 --cidr <your-vpc-cidr>
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type m5.large \
--key-name my-key-pair \
--security-group-ids "$SG" \
--subnet-id subnet-0123456789abcdef0 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=dnscrypt-proxy}]'You do not need --metadata-options, because the AMI already makes IMDSv2 required.
Connect and read the instance details
Connect as ubuntu with the key pair you chose:
ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>First boot finishes within a minute or two of launch. It writes the client allow-list from your VPC's CIDR blocks, generates the dashboard password, downloads and checks the signed resolver list, starts dnscrypt-proxy, and points the instance's own DNS at it.
The details are in /root/dnscrypt-proxy-credentials.txt, which only root can read. This command shows everything except the dashboard password:
sudo grep -v '^MONITORING_UI_PASSWORD=' /root/dnscrypt-proxy-credentials.txt# dnscrypt-proxy 2.1.20 - generated on first boot by dnscrypt-proxy-firstboot.service.
# The monitoring dashboard ships DISABLED. Its basic-auth password below was generated on this instance and no
# other instance shares it. To use the dashboard, enable it in /etc/dnscrypt-proxy/dnscrypt-proxy.toml and reach
# it through an SSH tunnel (see the guide). Keep this file private. MONITORING_UI_URL=http://127.0.0.1:8080/ MONITORING_UI_USER=admin DNS_LISTEN=[::]:53 (UDP and TCP) DNS_PRIVATE_ADDRESS=172.31.92.201 ALLOWED_CLIENTS=172.31.0.0/16 (+ loopback; /etc/dnscrypt-proxy/allowed-clients.txt) INSTANCE_HOSTNAME=ip-172-31-92-201.ec2.internal CONFIG=/etc/dnscrypt-proxy/dnscrypt-proxy.toml SELF_TEST=sudo dnscrypt-proxy-selftest GUIDE=https://secureanycloud.com/stacks/dnscrypt-proxy-2-1-20/DNS_PRIVATE_ADDRESS is the address your clients use. Your values differ from the example output.
Confirm dnscrypt-proxy is running
Confirm that dnscrypt-proxy and the allow-list are active and that first boot has completed. dnscrypt-proxy owns port 53 over UDP and TCP on every interface (* is IPv4 and IPv6):
systemctl is-active dnscrypt-proxy.service sac-dnscrypt-acl.service dnscrypt-proxy -version sudo ss -Htulnp 'sport = :53' | awk '{print $1, $5}'active active 2.1.20 udp *:53 tcp *:53Run the self-test
The shipped self-test checks the resolver end to end from the instance itself. It confirms the following:
- dnscrypt-proxy runs as its own user.
- The allow-list is loaded.
- A name resolves over UDP and TCP.
- Encrypted upstreams are in use.
- The instance resolves through its own resolver.
- Query logging is off.
- The instance metadata service is unreachable from inside dnscrypt-proxy, while the Amazon resolver answers there.
Run the self-test:
sudo dnscrypt-proxy-selftestok: dnscrypt-proxy is active (pid 2925, user dnscrypt-proxy) ok: port-53 allow-list loaded: 172.31.0.0/16 (+ loopback) ok: example.com resolves over UDP and TCP on 127.0.0.1:53 (172.66.147.243) ok: encrypted upstreams in use: 313 ] OK (DNSCrypt); 155 ] OK (DoH); ok: this instance resolves through its own encrypted resolver (/etc/resolv.conf -> 127.0.0.1) ok: query logging and NX logging are off ok: metadata: 169.254.169.254 is unreachable from inside dnscrypt-proxy; the Amazon resolver answers there SELFTEST_OKQuery the resolver
Names resolve over UDP and TCP. A name whose DNSSEC signature is deliberately broken (dnssec-failed.org) is refused with SERVFAIL, because only DNSSEC-validating resolvers are used. Check the log to see which encrypted resolver answered fastest at start-up:
dig +short @127.0.0.1 example.com A dig +tcp +short @127.0.0.1 example.com A dig @127.0.0.1 dnssec-failed.org A | grep -o 'status: [A-Z]*'
sudo journalctl -u dnscrypt-proxy.service --no-pager | grep -a 'lowest initial latency' | tail -1 | sed 's/^.*\]: //'172.66.147.243 104.20.23.154 172.66.147.243 104.20.23.154 status: SERVFAIL [2026-10-27 00:15:46] [NOTICE] Server with the lowest initial latency: quad9-dnscrypt-ip4-nofilter-pri (rtt: 0ms), live servers: 336Serve your VPC
The allow-list already contains your VPC's CIDR blocks, and the security group decides whether DNS traffic reaches the instance at all. Open UDP and TCP 53 to your VPC (or to the subnets of your clients) in the instance's security group, and never to 0.0.0.0/0:
aws ec2 authorize-security-group-ingress --group-id <your-sg> --protocol udp --port 53 --cidr <your-vpc-cidr>
aws ec2 authorize-security-group-ingress --group-id <your-sg> --protocol tcp --port 53 --cidr <your-vpc-cidr>Then, from any instance in the VPC, query the resolver's private address (DNS_PRIVATE_ADDRESS from Step 2):
dig +short @<instance-private-ip> example.com ATo make every instance in the VPC use the resolver, create a DHCP options set with domain-name-servers set to the resolver's private address, and associate it with the VPC. For resilience, use two resolvers in different Availability Zones. Alternatively, set the address in each client's resolver configuration. Keep the Amazon resolver for anything that needs it, because this image already sends AWS names there (Step 8).
A client outside the allow-list gets no answer at all, even if the security group lets its packets in. The instance drops and counts its queries. Show the current list and the drop counter:
sudo grep -v '^#' /etc/dnscrypt-proxy/allowed-clients.txt
sudo dnscrypt-proxy-acl show172.31.0.0/16 table inet sac_dns { set allow4 { type ipv4_addr flags interval elements = { 127.0.0.0/8, 172.31.0.0/16 } } set allow6 { type ipv6_addr flags interval elements = { ::1 } } chain input { type filter hook input priority filter - 10; policy accept; iifname "lo" accept meta l4proto { tcp, udp } th dport 53 ip saddr @allow4 accept meta l4proto { tcp, udp } th dport 53 ip6 saddr @allow6 accept meta l4proto { tcp, udp } th dport 53 counter packets 2 bytes 160 drop } }Allow more client networks
Clients outside the VPC range (a VPN pool, a peered VPC, an on-premises network over Direct Connect) need their range on the allow-list. Add one CIDR per line and apply it. The change is atomic and needs no restart:
echo '10.8.0.0/24' | sudo tee -a /etc/dnscrypt-proxy/allowed-clients.txt
sudo dnscrypt-proxy-acl applydnscrypt-proxy-acl refuses 0.0.0.0/0 and ::/0, which would turn the instance into an open resolver. Allow the same range in the security group as well.
AWS names and private hosted zones
EC2 host names (*.internal) and AWS service endpoints (amazonaws.com) are forwarded to the Amazon VPC resolver, so instance names, Systems Manager and VPC interface endpoints with private DNS resolve exactly as they do without dnscrypt-proxy. The rules are in /etc/dnscrypt-proxy/forwarding-rules.txt. The following command resolves the instance's own private name through dnscrypt-proxy:
sudo grep -v '^#' /etc/dnscrypt-proxy/forwarding-rules.txt
TOKEN=$(curl -s -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 60' http://169.254.169.254/latest/api/token)
LH=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/local-hostname)
echo "$LH"; dig +short @127.0.0.1 "$LH" Ainternal 169.254.169.253 amazonaws.com 169.254.169.253 ip-172-31-92-201.ec2.internal 172.31.92.201If your VPC uses Route 53 private hosted zones, add one line per zone. For example, corp.example.internal 169.254.169.253 is already covered by internal, while example.com 169.254.169.253 would send a whole private zone to the VPC resolver. Then restart dnscrypt-proxy:
echo 'example.com 169.254.169.253' | sudo tee -a /etc/dnscrypt-proxy/forwarding-rules.txt
sudo systemctl restart dnscrypt-proxyThe instance's own DNS
After first boot, the instance resolves through its own encrypted resolver and keeps the VPC search domain:
grep -v '^#' /etc/resolv.confnameserver 127.0.0.1 options edns0 trust-ad search ec2.internalTo send the instance's own lookups to the VPC resolver again, run sudo ln -sfn /run/systemd/resolve/resolv.conf /etc/resolv.conf. dnscrypt-proxy keeps serving your clients either way.
The monitoring dashboard (optional)
dnscrypt-proxy has a built-in monitoring dashboard (query counts, cache hit rate, resolver latencies). It is disabled on this image. To use it, set enabled = true in the [monitoring_ui] section of /etc/dnscrypt-proxy/dnscrypt-proxy.toml, and restart dnscrypt-proxy.
The dashboard listens on 127.0.0.1:8080 only. Reach it through an SSH tunnel from your workstation, and open http://127.0.0.1:8080/:
sudo sed -i '/^\[monitoring_ui\]/,/^\[/ s/^enabled = false/enabled = true/' /etc/dnscrypt-proxy/dnscrypt-proxy.toml
sudo systemctl restart dnscrypt-proxy ssh -i ~/.ssh/my-key-pair.pem -L 8080:127.0.0.1:8080 ubuntu@<public-ip>Sign in as admin with the password that was generated on your instance. Read it on the instance with:
sudo grep '^MONITORING_UI_PASSWORD=' /root/dnscrypt-proxy-credentials.txt | cut -d= -f2-On this image the dashboard shows aggregate figures only and keeps no list of recent queries (privacy_level = 2, enable_query_log = false). Keep it on 127.0.0.1.
Metadata access is blocked
dnscrypt-proxy cannot reach the instance metadata service. The Amazon resolver (169.254.169.253) is deliberately not blocked:
systemctl show -p IPAddressDeny dnscrypt-proxy.serviceIPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128How the fail-closed start works
dnscrypt-proxy.service runs only when first boot's ready marker exists and only together with the allow-list unit. Every start runs a check as root first, which confirms that the allow-list is loaded, the configuration parses, the dashboard password is a real one, and no forwarding rule points at the metadata service. Only root or the service can read the files that hold per-instance data:
systemctl cat dnscrypt-proxy.service | grep -E '^(ConditionPathExists|Requires|ExecStartPre|ExecStart|User|AmbientCapabilities)='
sudo stat -c '%a %U:%G %n' /root/dnscrypt-proxy-credentials.txt /etc/dnscrypt-proxy/dnscrypt-proxy.toml /etc/dnscrypt-proxy/allowed-clients.txtRequires=sac-dnscrypt-acl.service ConditionPathExists=/var/lib/sac/data/dnscrypt-proxy-ready User=dnscrypt-proxy ExecStartPre=+/usr/local/sbin/dnscrypt-proxy-preflight ExecStart=/usr/local/bin/dnscrypt-proxy -config /etc/dnscrypt-proxy/dnscrypt-proxy.toml AmbientCapabilities=CAP_NET_BIND_SERVICE 600 root:root /root/dnscrypt-proxy-credentials.txt 640 root:dnscrypt-proxy /etc/dnscrypt-proxy/dnscrypt-proxy.toml 644 root:root /etc/dnscrypt-proxy/allowed-clients.txtSecurity recommendations
- Keep SSH open only to your management addresses. Open UDP and TCP 53 only to the networks the resolver serves, never to
0.0.0.0/0. - Run two resolvers in different Availability Zones if your VPC depends on them, and list both in the DHCP options set.
- Leave query logging off unless you need it. If you turn it on, treat the log as sensitive and rotate it.
- Leave the dashboard off, or on
127.0.0.1behind an SSH tunnel. - Leave unattended security upgrades on. dnscrypt-proxy is a single binary in /usr/local/bin/dnscrypt-proxy, and SecureAnyCloud ships updated versions as new versions of this listing.
Support and licensing
dnscrypt-proxy is open-source software by Frank Denis and contributors, distributed under the ISC licence. The licence, the upstream example configuration and rule files, and SecureAnyCloud's build record (release, signature key and checksums) are in /usr/share/doc/dnscrypt-proxy/.
SecureAnyCloud provides the packaging, the secure defaults, the VPC allow-list, the AWS name forwarding, the per-instance dashboard password, the metadata block, the fail-closed start, the self-test and helpers, and 24/7 support.
SecureAnyCloud is not affiliated with or endorsed by the DNSCrypt project. dnscrypt-proxy is used here only to identify the software. Quad9 and other resolver names shown are those of their operators.
For help with deployment or configuration, email sales@secureanycloud.com or visit SecureAnyCloud support.
Deploy on AWS
Launch dnscrypt-proxy 2.1.20 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com