Certificates

Use the Certificates resource to manage SSL/TLS certificates for custom domains on your Fly Apps. Fly.io can automatically issue Let’s Encrypt certificates (ACME), or you can import your own custom certificates. When both exist for a hostname, the custom certificate is served as primary and the ACME certificate acts as an automatic fallback. Learn more about custom domains.

List certificates

GET /apps/{app_name}/certificates

List all certificates for an app, with optional filtering and cursor-based pagination.

Path parameters
app_name : string required

The name of the Fly App.

Query parameters
filter : string

Filter certificates by hostname substring.

cursor : string

Pagination cursor from a previous response.

limit : integer

Maximum number of certificates to return.

Responses
200 :

OK

GET/v1/apps/\{app_name\}/certificates
curl -i -X GET \
    -H "Authorization: Bearer ${FLY_API_TOKEN}" -H "Content-Type: application/json" \
    "${FLY_API_HOSTNAME}/v1/apps/my-app-name/certificates"
Status: 200 OK - Example response
{
  "certificates": [
    {
      "hostname": "example.com",
      "status": "active",
      "dns_provider": "enom",
      "acme_dns_configured": true,
      "acme_alpn_configured": true,
      "acme_http_configured": true,
      "ownership_txt_configured": true,
      "configured": true,
      "acme_requested": true,
      "has_custom_certificate": false,
      "has_fly_certificate": true,
      "created_at": "2024-01-15T10:30:00Z",
      "updated_at": "2024-01-15T12:00:00Z"
    }
  ],
  "total_count": 1
}

Request ACME certificate

POST /apps/{app_name}/certificates/acme

Add a hostname to your app and request an automatic Let’s Encrypt certificate. Fly.io will attempt to validate domain ownership and issue a certificate. Check the dns_requirements in the response to see what DNS records you need to configure.

Path parameters
app_name : string required

The name of the Fly App.

Responses
201 :

created

POST/v1/apps/\{app_name\}/certificates/acme
curl -i -X POST \
    -H "Authorization: Bearer ${FLY_API_TOKEN}" -H "Content-Type: application/json" \
    "${FLY_API_HOSTNAME}/v1/apps/my-app-name/certificates/acme" \
  -d '{
      "hostname": "example.com"
    }'
Status: 201 created - Example response
{
  "hostname": "example.com",
  "configured": false,
  "acme_requested": true,
  "status": "pending_validation",
  "dns_provider": "enom",
  "rate_limited_until": null,
  "certificates": [],
  "validation": {
    "dns_configured": false,
    "alpn_configured": false,
    "http_configured": false,
    "ownership_txt_configured": false
  },
  "dns_requirements": {
    "a": ["137.66.XXX.XXX"],
    "aaaa": ["2a09:8280:1::XXXX"],
    "cname": "my-app-name.fly.dev",
    "acme_challenge": {
      "name": "_acme-challenge.example.com",
      "target": "example.com.XXXXX.flydns.net"
    },
    "ownership": {
      "name": "_fly-ownership.example.com",
      "app_value": "app-XXXXXXXXXX",
      "org_value": "org-XXXXXXXXXX"
    }
  },
  "validation_errors": []
}

Import custom certificate

POST /apps/{app_name}/certificates/custom

Upload your own certificate and private key in PEM format. The certificate must not be expired, the private key must match the certificate, and the hostname must appear in the certificate’s Subject Alternative Names (SAN) or Common Name (CN). Wildcard certificates are supported.

Domain ownership must be verified via a _fly-ownership DNS TXT record before the certificate becomes active. Check the dns_requirements.ownership field in the response for the required record.

Path parameters
app_name : string required

The name of the Fly App.

Responses
201 :

created

POST/v1/apps/\{app_name\}/certificates/custom
curl -i -X POST \
    -H "Authorization: Bearer ${FLY_API_TOKEN}" -H "Content-Type: application/json" \
    "${FLY_API_HOSTNAME}/v1/apps/my-app-name/certificates/custom" \
  -d '{
      "hostname": "example.com",
      "fullchain": "-----BEGIN CERTIFICATE-----\nMIID...\n-----END CERTIFICATE-----",
      "private_key": "-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----"
    }'
Status: 201 created - Example response
{
  "hostname": "example.com",
  "configured": false,
  "acme_requested": false,
  "status": "pending_ownership",
  "dns_provider": "enom",
  "rate_limited_until": null,
  "certificates": [
    {
      "source": "custom",
      "status": "pending_ownership",
      "created_at": "2024-01-15T10:30:00Z",
      "expires_at": "2039-01-15T10:30:00Z",
      "issuer": "Cloudflare Origin Certificate",
      "issued": []
    }
  ],
  "validation": {
    "dns_configured": false,
    "alpn_configured": false,
    "http_configured": false,
    "ownership_txt_configured": false
  },
  "dns_requirements": {
    "a": ["137.66.XXX.XXX"],
    "aaaa": ["2a09:8280:1::XXXX"],
    "cname": "my-app-name.fly.dev",
    "acme_challenge": {
      "name": "_acme-challenge.example.com",
      "target": "example.com.XXXXX.flydns.net"
    },
    "ownership": {
      "name": "_fly-ownership.example.com",
      "app_value": "app-XXXXXXXXXX",
      "org_value": "org-XXXXXXXXXX"
    }
  },
  "validation_errors": []
}

Get certificate details

GET /apps/{app_name}/certificates/{hostname}

Get detailed information about a hostname’s certificates, including validation status, DNS requirements, and any validation errors.

For wildcard hostnames, URL-encode the * character (e.g. %2A.example.com).

Path parameters
app_name : string required

The name of the Fly App.

hostname : string required

The hostname to get certificate details for.

Responses
200 :

OK

GET/v1/apps/\{app_name\}/certificates/\{hostname\}
curl -i -X GET \
    -H "Authorization: Bearer ${FLY_API_TOKEN}" -H "Content-Type: application/json" \
    "${FLY_API_HOSTNAME}/v1/apps/my-app-name/certificates/example.com"
Status: 200 OK - Example response
{
  "hostname": "example.com",
  "configured": true,
  "acme_requested": true,
  "status": "active",
  "dns_provider": "enom",
  "rate_limited_until": null,
  "certificates": [
    {
      "source": "custom",
      "status": "active",
      "created_at": "2024-01-15T10:30:00Z",
      "expires_at": "2039-01-15T10:30:00Z",
      "issuer": "Cloudflare Origin Certificate",
      "issued": []
    },
    {
      "source": "fly",
      "status": "active",
      "created_at": "2024-01-15T12:00:00Z",
      "expires_at": "2024-04-15T12:00:00Z",
      "issuer": null,
      "issued": [
        {
          "type": "ecdsa",
          "expires_at": "2024-04-15T12:00:00Z",
          "certificate_authority": "lets_encrypt"
        }
      ]
    }
  ],
  "validation": {
    "dns_configured": true,
    "alpn_configured": true,
    "http_configured": true,
    "ownership_txt_configured": true
  },
  "dns_requirements": {
    "a": ["137.66.XXX.XXX"],
    "aaaa": ["2a09:8280:1::XXXX"],
    "cname": "my-app-name.fly.dev",
    "acme_challenge": {
      "name": "_acme-challenge.example.com",
      "target": "example.com.XXXXX.flydns.net"
    },
    "ownership": {
      "name": "_fly-ownership.example.com",
      "app_value": "app-XXXXXXXXXX",
      "org_value": "org-XXXXXXXXXX"
    }
  },
  "validation_errors": []
}

Check certificate status

POST /apps/{app_name}/certificates/{hostname}/check

Trigger a fresh DNS validation check for a hostname. Returns the same details as the get endpoint, plus actual DNS records resolved for the hostname. Use this to diagnose DNS configuration issues.

Path parameters
app_name : string required

The name of the Fly App.

hostname : string required

The hostname to check.

Responses
200 :

OK

POST/v1/apps/\{app_name\}/certificates/\{hostname\}/check
curl -i -X POST \
    -H "Authorization: Bearer ${FLY_API_TOKEN}" -H "Content-Type: application/json" \
    "${FLY_API_HOSTNAME}/v1/apps/my-app-name/certificates/example.com/check"
Status: 200 OK - Example response
{
  "hostname": "example.com",
  "configured": true,
  "acme_requested": true,
  "status": "active",
  "dns_provider": "enom",
  "rate_limited_until": null,
  "certificates": [
    {
      "source": "fly",
      "status": "active",
      "created_at": "2024-01-15T12:00:00Z",
      "expires_at": "2024-04-15T12:00:00Z",
      "issuer": null,
      "issued": [
        {
          "type": "ecdsa",
          "expires_at": "2024-04-15T12:00:00Z",
          "certificate_authority": "lets_encrypt"
        }
      ]
    }
  ],
  "validation": {
    "dns_configured": true,
    "alpn_configured": true,
    "http_configured": true,
    "ownership_txt_configured": true
  },
  "dns_requirements": {
    "a": ["137.66.XXX.XXX"],
    "aaaa": ["2a09:8280:1::XXXX"],
    "cname": "my-app-name.fly.dev",
    "acme_challenge": {
      "name": "_acme-challenge.example.com",
      "target": "example.com.XXXXX.flydns.net"
    },
    "ownership": {
      "name": "_fly-ownership.example.com",
      "app_value": "app-XXXXXXXXXX",
      "org_value": "org-XXXXXXXXXX"
    }
  },
  "validation_errors": [],
  "dns_records": {
    "a": ["137.66.XXX.XXX"],
    "aaaa": ["2a09:8280:1::XXXX"],
    "cname": [],
    "resolved_addresses": ["137.66.XXX.XXX", "2a09:8280:1::XXXX"],
    "soa": "ns1.example.com",
    "acme_challenge_cname": "example.com.XXXXX.flydns.net",
    "ownership_txt": "app-XXXXXXXXXX"
  }
}

Delete hostname and all certificates

DELETE /apps/{app_name}/certificates/{hostname}

Remove a hostname and all associated certificates (both ACME and custom) from the app.

Path parameters
app_name : string required

The name of the Fly App.

hostname : string required

The hostname to remove.

Responses
204 :

no content

DELETE/v1/apps/\{app_name\}/certificates/\{hostname\}
curl -i -X DELETE \
    -H "Authorization: Bearer ${FLY_API_TOKEN}" -H "Content-Type: application/json" \
    "${FLY_API_HOSTNAME}/v1/apps/my-app-name/certificates/example.com"
Status: 204 no content
no body

Delete ACME certificates

DELETE /apps/{app_name}/certificates/{hostname}/acme

Stop ACME certificate issuance for a hostname. If a custom certificate exists, it will continue to be served. The hostname itself is not removed.

Path parameters
app_name : string required

The name of the Fly App.

hostname : string required

The hostname to stop ACME issuance for.

Responses
200 :

OK

DELETE/v1/apps/\{app_name\}/certificates/\{hostname\}/acme
curl -i -X DELETE \
    -H "Authorization: Bearer ${FLY_API_TOKEN}" -H "Content-Type: application/json" \
    "${FLY_API_HOSTNAME}/v1/apps/my-app-name/certificates/example.com/acme"
Status: 200 OK - Example response
{
  "hostname": "example.com",
  "configured": true,
  "acme_requested": false,
  "status": "active",
  "certificates": [
    {
      "source": "custom",
      "status": "active",
      "created_at": "2024-01-15T10:30:00Z",
      "expires_at": "2039-01-15T10:30:00Z",
      "issuer": "Cloudflare Origin Certificate",
      "issued": []
    }
  ],
  "validation": {
    "dns_configured": true,
    "alpn_configured": false,
    "http_configured": false,
    "ownership_txt_configured": true
  },
  "dns_requirements": { "..." : "..." },
  "validation_errors": []
}

Delete custom certificate

DELETE /apps/{app_name}/certificates/{hostname}/custom

Remove the custom certificate for a hostname. If ACME certificates are configured, they will continue to be served. The hostname itself is not removed.

Path parameters
app_name : string required

The name of the Fly App.

hostname : string required

The hostname to remove the custom certificate from.

Responses
200 :

OK

DELETE/v1/apps/\{app_name\}/certificates/\{hostname\}/custom
curl -i -X DELETE \
    -H "Authorization: Bearer ${FLY_API_TOKEN}" -H "Content-Type: application/json" \
    "${FLY_API_HOSTNAME}/v1/apps/my-app-name/certificates/example.com/custom"
Status: 200 OK - Example response
{
  "hostname": "example.com",
  "configured": true,
  "acme_requested": true,
  "status": "active",
  "certificates": [
    {
      "source": "fly",
      "status": "active",
      "created_at": "2024-01-15T12:00:00Z",
      "expires_at": "2024-04-15T12:00:00Z",
      "issuer": null,
      "issued": [
        {
          "type": "ecdsa",
          "expires_at": "2024-04-15T12:00:00Z",
          "certificate_authority": "lets_encrypt"
        }
      ]
    }
  ],
  "validation": {
    "dns_configured": true,
    "alpn_configured": true,
    "http_configured": true,
    "ownership_txt_configured": true
  },
  "dns_requirements": { "..." : "..." },
  "validation_errors": []
}