Compare commits
1
Commits
main
...
feat/README
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa568ada08 |
@@ -0,0 +1,133 @@
|
||||
# dokploy
|
||||
|
||||
Terraform + Ansible setup for a self-hosted [Dokploy](https://dokploy.com/) instance.
|
||||
|
||||
- **Terraform** provisions a Hetzner Cloud VPS, registers an SSH key, creates a Cloudflare DNS record, and writes the Ansible inventory.
|
||||
- **Ansible** configures the host afterwards (base / users / security roles).
|
||||
|
||||
## Repository layout
|
||||
|
||||
```
|
||||
.
|
||||
├── terraform/
|
||||
│ ├── main.tf
|
||||
│ ├── providers.tf
|
||||
│ ├── variables.tf
|
||||
│ ├── terraform.tfvars # non-secret config (committed)
|
||||
│ ├── .env.example # template for required secrets (committed)
|
||||
│ ├── .env # real secrets (gitignored — created locally)
|
||||
│ └── .envrc # direnv loader (committed)
|
||||
└── ansible/
|
||||
├── playbook.yml
|
||||
├── requirements.yml
|
||||
└── group_vars/
|
||||
```
|
||||
|
||||
## Secrets
|
||||
|
||||
Secrets are **never** committed and never written into `.tf` or `.tfvars` files.
|
||||
They live only in `terraform/.env` and are injected as `TF_VAR_*` environment
|
||||
variables, which Terraform maps to input variables automatically.
|
||||
|
||||
| Variable | Environment variable | Committed to git |
|
||||
| --- | --- | --- |
|
||||
| `hcloud_token` | `TF_VAR_hcloud_token` | No |
|
||||
| `cloudflare_api_token` | `TF_VAR_cloudflare_api_token` | No |
|
||||
| `domain_name` | `TF_VAR_domain_name` | No |
|
||||
|
||||
Everything else (server type, image, location, project name, SSH public key,
|
||||
DNS record name, proxied flag) is non-secret and lives in `terraform/terraform.tfvars`.
|
||||
|
||||
### `.env` format
|
||||
|
||||
`terraform/.env`:
|
||||
|
||||
```env
|
||||
TF_VAR_hcloud_token=...
|
||||
TF_VAR_cloudflare_api_token=...
|
||||
TF_VAR_domain_name=...
|
||||
```
|
||||
|
||||
## Local setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Terraform `>= 1.0`
|
||||
- [direnv](https://direnv.net/) (recommended)
|
||||
- A Hetzner Cloud API token
|
||||
- A Cloudflare API token with `Zone.DNS` edit permission
|
||||
|
||||
### Steps
|
||||
|
||||
```sh
|
||||
# 1. Create your local secrets file from the template, then fill in the values.
|
||||
cp terraform/.env.example terraform/.env
|
||||
|
||||
# 2. Allow direnv to load it (evaluates terraform/.envrc).
|
||||
cd terraform
|
||||
direnv allow
|
||||
|
||||
# 3. Provision.
|
||||
terraform init
|
||||
terraform plan
|
||||
terraform apply
|
||||
```
|
||||
|
||||
`terraform/.envrc` loads the `.env` file:
|
||||
|
||||
```sh
|
||||
dotenv_if_exists .env
|
||||
```
|
||||
|
||||
direnv loads the variables when you `cd` into `terraform/` and unloads them when
|
||||
you leave, so secrets never linger in your shell.
|
||||
|
||||
### Without direnv
|
||||
|
||||
Load the file manually, scoped to a single command:
|
||||
|
||||
```sh
|
||||
cd terraform
|
||||
set -a; . ./.env; set +a
|
||||
terraform plan
|
||||
```
|
||||
|
||||
Or use a wrapper that keeps the variables contained to the `terraform` subprocess:
|
||||
|
||||
```sh
|
||||
#!/bin/sh
|
||||
set -a
|
||||
. ./.env
|
||||
set +a
|
||||
exec terraform "$@"
|
||||
```
|
||||
|
||||
## CI/CD
|
||||
|
||||
CI reads the same variables from its secret store — no `.env` file is needed.
|
||||
|
||||
GitHub Actions example:
|
||||
|
||||
```yaml
|
||||
env:
|
||||
TF_VAR_hcloud_token: ${{ secrets.TF_VAR_HCLOUD_TOKEN }}
|
||||
TF_VAR_cloudflare_api_token: ${{ secrets.TF_VAR_CLOUDFLARE_API_TOKEN }}
|
||||
TF_VAR_domain_name: ${{ secrets.TF_VAR_DOMAIN_NAME }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: hashicorp/setup-terraform@v3
|
||||
- run: terraform init
|
||||
- run: terraform plan
|
||||
- run: terraform apply -auto-approve
|
||||
```
|
||||
|
||||
## Security notes
|
||||
|
||||
- Never commit `terraform/.env` — it is listed in `.gitignore`.
|
||||
- `.env.example` is a template only; it must never contain real values.
|
||||
- If a token is ever exposed (commit, paste, logs, screenshots), rotate it
|
||||
immediately in the Hetzner / Cloudflare consoles and update `.env`.
|
||||
- Terraform state files (`*.tfstate`) are gitignored; they can contain derived
|
||||
values such as the resulting DNS hostname.
|
||||
- For reproducible CI runs, consider committing `terraform/.terraform.lock.hcl`.
|
||||
Reference in New Issue
Block a user