Terraform
Deploy a complete Defguard 2.x environment on AWS with Terraform: EC2 instances for Core, Edge and Gateway plus a managed RDS PostgreSQL database.
This guide deploys a full Defguard 2.x environment on Amazon Web Services using Terraform. The provided configuration provisions everything you need to run Defguard:
three EC2 instances, one each for Defguard Core, Defguard Edge and Defguard Gateway,
a managed RDS PostgreSQL database for Core,
the supporting networking (security groups, network interfaces, Elastic IPs) and, optionally, a complete VPC.
The instances bootstrap themselves on first boot: each one downloads the matching Defguard .deb release, writes its configuration file and starts its systemd service. Core then auto-adopts the Edge and Gateway over gRPC, so the environment comes up wired together with no manual component registration.
Use this method when you want a repeatable, infrastructure-as-code deployment on AWS. If you are evaluating Defguard for the first time, the one-line install script is faster. For other platforms you can see Docker Compose or Kubernetes.
Before you start, make sure you understand Defguard's architecture, in particular the roles of the three components: Core, Edge and Gateway.
Architecture
The deployment places the control plane (Core) and the database in private subnets, and exposes only the two components that clients must reach (Edge and Gateway) through public Elastic IPs.
Core
private
No (EIP-less)
HTTP 8000 (VPC-internal), gRPC 50055
Control plane: web UI, authentication, adopts Edge and Gateway.
Edge
public
Yes (Elastic IP)
HTTPS 443, ACME 80 (public); HTTP 8080, gRPC 50051 (VPC)
User enrollment and client communication.
Gateway
public
Yes (Elastic IP)
WireGuard UDP 51820 (public); gRPC 50066 (from Core)
WireGuard VPN endpoint for clients.
Database
private
No
PostgreSQL 5432 (from Core only)
Managed RDS PostgreSQL, SSL-enforced and encrypted.
Traffic flow:
VPN clients connect to the Gateway's public IP on
51820/udp.Enrolling users reach the Edge's public IP on
443/tcp.Core stays private. It egresses through a NAT gateway (to download packages and reach the license server) and dials the Edge and Gateway over gRPC on boot to adopt them and issue mTLS certificates.
The database accepts connections only from Core's security group.
Core has no public IP by design. To reach its web UI you either need access to the VPC (an existing VPN, VPC peering or a bastion) or you tunnel through the public Gateway over SSH. See Outputs and first login.
Prerequisites
An AWS account with permissions to create EC2, RDS, VPC, Elastic IP and security-group resources.
Terraform >= 1.5.0.
AWS credentials available through the standard provider chain:
aws configure, anAWS_PROFILE, SSO, an instance role, orAWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYenvironment variables.An EC2 key pair in the target region if you want SSH access (recommended, as the Gateway doubles as a jump host to the private Core).
Your administrator public IP as a
/32CIDR forssh_admin_cidr(find it withcurl -s https://checkip.amazonaws.com).
Never hardcode or commit AWS access keys. The .env file in the example directory contains placeholder credentials for demonstration only. Use the AWS provider chain (environment variables, a shared profile or SSO) instead.
Get the Terraform code
The Terraform modules and worked examples live in the Defguard deployment repository, under terraform2.0/.
Two example configurations are provided:
examples/basic- creates a brand new VPC (subnets, NAT gateway, internet gateway) and deploys Defguard into it.examples/existing-vpc- deploys Defguard into a VPC and subnets you already operate.
Pick the one that matches your environment.
Option A (examples/basic)
Use examples/basic when you want Terraform to build the whole network from scratch. It creates a VPC with the official terraform-aws-modules/vpc/aws module (public and private subnets, a single NAT gateway, an internet gateway) and then deploys all Defguard components.
The main.tf files are not committed; the repository ships main.tf.example templates. Copy the example to main.tf and edit your copy:
Then edit the locals block at the top of main.tf to fit your account. The key values:
Pick a vpc_cidr that does not overlap the WireGuard location address range that Core auto-creates during adoption (the default is 10.0.0.0/24). The example uses 10.20.0.0/16 for exactly this reason.
Then provision:
Option B: deploy into an existing VPC
Use examples/existing-vpc to deploy into a VPC you already run. This example does not create a VPC; it creates only the security groups, network interfaces, Elastic IPs, the RDS database and the three component instances.
Your network must satisfy these requirements:
core_subnet_id- a private subnet with outbound internet (a NAT gateway or instance). Core has no public IP and must download its package and reach the license server on first boot.public_subnet_id- a public subnet (internet-gateway route) for the Gateway and Edge Elastic IPs. To split them across subnets, also setgateway_subnet_idandedge_subnet_id.db_subnet_ids- at least two subnets in different availability zones (an RDS subnet-group requirement). Private subnets are recommended.
The main.tf files are not committed; copy the shipped template to main.tf first:
Component settings (ports, versions, instance types, database) are in the locals block of main.tf, identical to the examples/basic example.
Then provide your network details in examples/existing-vpc/terraform.tfvars (replace every value below with your own):
Then provision:
Outputs and first login
Both examples emit the same outputs:
defguard_core_private_address
Private IP of Core (no public access).
defguard_edge_public_address
Public IP of Edge (enrollment / client HTTPS).
defguard_edge_private_address
Private IP of Edge.
defguard_gateway_public_address
Public IP of Gateway (WireGuard endpoint for clients).
defguard_gateway_private_address
Private IP of Gateway.
Core is private, so reach its web UI in one of two ways:
If you already have VPC access (an existing VPN, peering or bastion), open
http://<defguard_core_private_address>:8000directly.Otherwise, tunnel through the public Gateway over SSH (requires
ssh_key_nameandssh_admin_cidrset):
Complete the Core setup wizard in the browser to create the admin account.
Post-deployment configuration
For the full list of tunable settings see the configuration reference, and validate the deployment end to end with the production deployment verification guide.
Module and variable reference
The examples compose four reusable modules from the deployment repository (terraform2.0/modules/*). The tables below list every input so you can build your own root module or customize the examples.
Network module
Provisions the security groups, network interfaces, Elastic IPs and the RDS database. Reused by both examples.
vpc_id
string
(required)
ID of the VPC the components are deployed into.
vpc_cidr
string
(required)
VPC CIDR, used for VPC-internal security-group rules (Core UI/SSH, Edge HTTP).
name_prefix
string
"defguard"
Prefix for created resource names. Change it to run multiple deployments per VPC.
public_subnet_id
string
(required)
Public subnet (IGW route) for the Gateway and Edge NICs.
gateway_subnet_id
string
null
Optional separate public subnet for the Gateway NIC.
edge_subnet_id
string
null
Optional separate public subnet for the Edge NIC.
core_subnet_id
string
(required)
Private subnet (NAT egress) for the Core NIC.
db_subnet_ids
list(string)
(required)
Subnets for the RDS subnet group; must span at least two AZs.
ssh_admin_cidr
string
null
CIDR allowed to SSH. null disables SSH; set a /32, avoid 0.0.0.0/0.
core_http_port
number
8000
Core web UI port (VPC-internal only).
gateway_grpc_port
number
50066
Gateway gRPC port that Core dials for adoption/control.
wireguard_port
number
51820
Public UDP port the WireGuard VPN listens on.
edge_grpc_port
number
50051
Edge gRPC port that Core dials for adoption/control.
edge_http_port
number
8080
Edge plain HTTP API port (VPC-internal only).
edge_https_port
number
443
Edge public HTTPS port.
db_name
string
"defguard"
Database name created for Core.
db_username
string
"defguard"
Database username.
db_password
string
(required)
Database password (sensitive).
db_port
number
5432
Database port.
db_engine_version
string
"18"
Major PostgreSQL version; the parameter-group family is derived from it.
db_storage
number
20
Allocated storage in GB (AWS minimum 20).
db_instance_class
string
"db.t3.micro"
RDS instance class.
Network module outputs: core_network_interface_id, gateway_network_interface_id, edge_network_interface_id, core_private_ip, gateway_private_ip, edge_private_ip, gateway_public_ip, edge_public_ip, and db_details (a sensitive object of name, username, password, port, address in the shape the Core module expects).
Core module
ami
string
(required)
Ubuntu AMI ID for the instance.
instance_type
string
"t3.micro"
EC2 instance type.
db_details
object
(required)
Database connection object (sensitive); typically module.network.db_details.
grpc_port
number
50055
Core gRPC server port.
http_port
number
8000
Core HTTP web UI port.
gateway_address
string
(required)
Address Core dials to adopt the Gateway (also reused as the WireGuard location endpoint).
gateway_grpc_port
number
50066
Gateway gRPC port.
edge_address
string
(required)
Address Core dials to adopt the Edge (internal only, private address preferred).
edge_grpc_port
number
50051
Edge gRPC port.
network_interface_id
string
(required)
ENI ID from the network module.
package_version
string
(required)
Defguard Core version to install (e.g. 2.0.1).
arch
string
(required)
Package architecture: x86_64 or aarch64.
cookie_insecure
bool
(required)
Allow insecure cookies (set true only for plain-HTTP access).
log_level
string
"info"
trace, debug, info, warn or error.
key_name
string
null
Existing EC2 key pair for SSH; null launches without a key.
Edge module
ami
string
(required)
Ubuntu AMI ID for the instance.
instance_type
string
"t3.micro"
EC2 instance type.
grpc_port
number
50051
Edge gRPC port (Core dials this).
http_port
number
8080
Edge enrollment HTTP port (VPC-internal).
https_port
number
443
Edge public HTTPS port.
network_interface_id
string
(required)
ENI ID from the network module.
arch
string
(required)
Package architecture: x86_64 or aarch64.
package_version
string
(required)
Defguard Edge (proxy) version to install.
log_level
string
"info"
Logging level.
key_name
string
null
Existing EC2 key pair for SSH; null for no key.
Gateway module
ami
string
(required)
Ubuntu AMI ID for the instance.
instance_type
string
"t3.micro"
EC2 instance type.
grpc_port
number
50066
Gateway gRPC port (Core dials this).
network_interface_id
string
(required)
ENI ID from the network module.
package_version
string
(required)
Defguard Gateway version to install.
arch
string
(required)
Package architecture: x86_64 or aarch64.
nat
bool
true
Enable masquerading (and IP forwarding) for clients.
key_name
string
null
Existing EC2 key pair for SSH; null for no key.
log_level
string
"info"
Logging level.
Security groups
The network module creates one security group per component:
Core
HTTP core_http_port from the VPC CIDR; SSH 22 from ssh_admin_cidr (only if set).
Gateway
WireGuard wireguard_port/udp from 0.0.0.0/0; gRPC gateway_grpc_port from Core's SG; SSH 22 if set.
Edge
HTTPS edge_https_port and ACME 80 from 0.0.0.0/0; HTTP edge_http_port from the VPC; gRPC from Core; SSH if set.
Database
PostgreSQL db_port from Core's SG only. SSL is forced via the parameter group (rds.force_ssl = 1).
All components allow full egress. Only the Gateway and Edge receive a public Elastic IP; Core and the database stay private.
What gets created and cost
A default deployment provisions, at minimum:
3 EC2 instances (default
t3.micro),1 RDS PostgreSQL instance (default
db.t3.micro, 20 GB, encrypted, 7-day backups),2 Elastic IPs (Gateway and Edge),
security groups and network interfaces,
and, in the
examples/basicexample, a VPC with a NAT gateway.
The default t3.micro / db.t3.micro sizes are intended for evaluation. For production, size the instances and database to your load and follow the hardware, OS, network and firewall recommendations. The NAT gateway and Elastic IPs incur ongoing AWS charges.
Destroy
To tear everything down:
Last updated
Was this helpful?