Git Product home page Git Product logo

timothymiller / cloudflare-ddns Goto Github PK

View Code? Open in Web Editor NEW
2.7K 21.0 281.0 537 KB

πŸŽ‰πŸŒ©οΈ Dynamic DNS (DDNS) service based on Cloudflare! Access your home network remotely via a custom domain name without a static IP!

Home Page: https://timknowsbest.com/free-dynamic-dns

License: GNU General Public License v3.0

Python 90.41% Shell 6.26% Dockerfile 3.32%
dynamic-dns self-hosted raspberry-pi ddns-client docker-image ipv6 cloudflare-ddns arm64 amd64 armv7

cloudflare-ddns's Introduction

Cloudflare DDNS

πŸš€ Cloudflare DDNS

Access your home network remotely via a custom domain name without a static IP!

⚑ Efficiency

  • ❀️ Easy config. List your domains and you're done.
  • πŸ” The Python runtime will re-use existing HTTP connections.
  • πŸ—ƒοΈ Cloudflare API responses are cached to reduce API usage.
  • 🀏 The Docker image is small and efficient.
  • 0️⃣ Zero dependencies.
  • πŸ’ͺ Supports all platforms.
  • 🏠 Enables low cost self hosting to promote a more decentralized internet.
  • πŸ”’ Zero-log IP provider (cdn-cgi/trace)
  • πŸ‘ GPL-3.0 License. Open source for open audits.

πŸ’― Complete Support of Domain Names, Subdomains, IPv4 & IPv6, and Load Balancing

  • 🌐 Supports multiple domains (zones) on the same IP.
  • πŸ“  Supports multiple subdomains on the same IP.
  • πŸ“‘ IPv4 and IPv6 support.
  • 🌍 Supports all Cloudflare regions.
  • βš–οΈ Supports Cloudflare Load Balancing.
  • πŸ‡ΊπŸ‡Έ Made in the U.S.A.

πŸ“Š Stats

Size Downloads Discord
cloudflare-ddns docker image size Total DockerHub pulls Official Discord Server

🚦 Getting Started

First copy the example configuration file into the real one.

cp config-example.json config.json

Edit config.json and replace the values with your own.

πŸ”‘ Authentication methods

You can choose to use either the newer API tokens, or the traditional API keys

To generate a new API tokens, go to your Cloudflare Profile and create a token capable of Edit DNS. Then replace the value in

"authentication":
  "api_token": "Your cloudflare API token, including the capability of **Edit DNS**"

Alternatively, you can use the traditional API keys by setting appropriate values for:

"authentication":
  "api_key":
    "api_key": "Your cloudflare API Key",
    "account_email": "The email address you use to sign in to cloudflare",

πŸ“ Enable or disable IPv4 or IPv6

Some ISP provided modems only allow port forwarding over IPv4 or IPv6. In this case, you would want to disable any interface not accessible via port forward.

"a": true,
"aaaa": true

πŸŽ›οΈ Other values explained

"zone_id": "The ID of the zone that will get the records. From your dashboard click into the zone. Under the overview tab, scroll down and the zone ID is listed in the right rail",
"subdomains": "Array of subdomains you want to update the A & where applicable, AAAA records. IMPORTANT! Only write subdomain name. Do not include the base domain name. (e.g. foo or an empty string to update the base domain)",
"proxied": "Defaults to false. Make it true if you want CDN/SSL benefits from cloudflare. This usually disables SSH)",
"ttl": "Defaults to 300 seconds. Longer TTLs speed up DNS lookups by increasing the chance of cached results, but a longer TTL also means that updates to your records take longer to go into effect. You can choose a TTL between 30 seconds and 1 day. For more information, see [Cloudflare's TTL documentation](https://developers.cloudflare.com/dns/manage-dns-records/reference/ttl/)",

πŸ“  Hosting multiple subdomains on the same IP?

This script can be used to update multiple subdomains on the same IP address.

For example, if you have a domain example.com and you want to host additional subdomains at foo.example.com and bar.example.com on the same IP address, you can use this script to update the DNS records for all subdomains.

⚠️ Note

Please remove the comments after // in the below example. They are only there to explain the config.

Do not include the base domain name in your subdomains config. Do not use the FQDN.

πŸ‘‰ Example πŸš€

{
  "cloudflare": [
    {
      "authentication": {
        "api_token": "api_token_here", // Either api_token or api_key
        "api_key": {
          "api_key": "api_key_here",
          "account_email": "your_email_here"
        }
      },
      "zone_id": "your_zone_id_here",
      "subdomains": [
        {
          "name": "", // Root domain (example.com)
          "proxied": true
        },
        {
          "name": "foo", // (foo.example.com)
          "proxied": true
        },
        {
          "name": "bar", // (bar.example.com)
          "proxied": true
        }
      ]
    }
  ],
  "a": true,
  "aaaa": true,
  "purgeUnknownRecords": false,
  "ttl": 300
}

🌐 Hosting multiple domains (zones) on the same IP?

You can handle ddns for multiple domains (cloudflare zones) using the same docker container by duplicating your configs inside the cloudflare: [] key within config.json like below:

⚠️ Note:

If you are using API Tokens, make sure the token used supports editing your zone ID.

{
  "cloudflare": [
    {
      "authentication": {
        "api_token": "api_token_here",
        "api_key": {
          "api_key": "api_key_here",
          "account_email": "your_email_here"
        }
      },
      "zone_id": "your_first_zone_id_here",
      "subdomains": [
        {
          "name": "",
          "proxied": false
        },
        {
          "name": "remove_or_replace_with_your_subdomain",
          "proxied": false
        }
      ]
    },
    {
      "authentication": {
        "api_token": "api_token_here",
        "api_key": {
          "api_key": "api_key_here",
          "account_email": "your_email_here"
        }
      },
      "zone_id": "your_second_zone_id_here",
      "subdomains": [
        {
          "name": "",
          "proxied": false
        },
        {
          "name": "remove_or_replace_with_your_subdomain",
          "proxied": false
        }
      ]
    }
  ],
  "a": true,
  "aaaa": true,
  "purgeUnknownRecords": false
}

βš–οΈ Load Balancing

If you have multiple IP addresses and want to load balance between them, you can use the loadBalancing option. This will create a CNAME record for each subdomain that points to the subdomain with the lowest IP address.

πŸ“œ Example config to support load balancing

{
  "cloudflare": [
    {
      "authentication": {
        "api_token": "api_token_here",
        "api_key": {
          "api_key": "api_key_here",
          "account_email": "your_email_here"
        }
      },
      "zone_id": "your_zone_id_here",
      "subdomains": [
        {
          "name": "",
          "proxied": false
        },
        {
          "name": "remove_or_replace_with_your_subdomain",
          "proxied": false
        }
      ]
    }
  ],{
  "cloudflare": [
    {
      "authentication": {
        "api_token": "api_token_here",
        "api_key": {
          "api_key": "api_key_here",
          "account_email": "your_email_here"
        }
      },
      "zone_id": "your_zone_id_here",
      "subdomains": [
        {
          "name": "",
          "proxied": false
        },
        {
          "name": "remove_or_replace_with_your_subdomain",
          "proxied": false
        }
      ]
    }
  ],
  "load_balancer": [
    {
      "authentication": {
        "api_token": "api_token_here",
        "api_key": {
          "api_key": "api_key_here",
          "account_email": "your_email_here"
        }
      },
      "pool_id": "your_pool_id_here",
      "origin": "your_origin_name_here"
    }
  ],
  "a": true,
  "aaaa": true,
  "purgeUnknownRecords": false,
  "ttl": 300
}

🧹 Optional features

purgeUnknownRecords removes stale DNS records from Cloudflare. This is useful if you have a dynamic DNS record that you no longer want to use. If you have a dynamic DNS record that you no longer want to use, you can set purgeUnknownRecords to true and the script will remove the stale DNS record from Cloudflare.

🐳 Deploy with Docker Compose

Pre-compiled images are available via the official docker container on DockerHub.

Modify the host file path of config.json inside the volumes section of docker-compose.yml.

version: '3.9'
services:
  cloudflare-ddns:
    image: timothyjmiller/cloudflare-ddns:latest
    container_name: cloudflare-ddns
    security_opt:
      - no-new-privileges:true
    network_mode: 'host'
    environment:
      - PUID=1000
      - PGID=1000
    volumes:
      - /YOUR/PATH/HERE/config.json:/config.json
    restart: unless-stopped

⚠️ IPv6

Docker requires network_mode be set to host in order to access the IPv6 public address.

πŸƒβ€β™‚οΈ Running

From the project root directory

docker-compose up -d

πŸ‹ Kubernetes

Create config File

cp ../../config-example.json config.json

Edit config.jsonon (vim, nvim, nano... )

${EDITOR} config.json

Create config file as Secret.

kubectl create secret generic config-cloudflare-ddns --from-file=config.json --dry-run=client -oyaml -n ddns > config-cloudflare-ddns-Secret.yaml

apply this secret

kubectl apply -f config-cloudflare-ddns-Secret.yaml
rm config.json # recomended Just keep de secret on Kubernetes Cluster

apply this Deployment

kubectl apply -f cloudflare-ddns-Deployment.yaml

🐧 Deploy with Linux + Cron

πŸƒ Running (all distros)

This script requires Python 3.5+, which comes preinstalled on the latest version of Raspbian. Download/clone this repo and give permission to the project's bash script by running chmod +x ./start-sync.sh. Now you can execute ./start-sync.sh, which will set up a virtualenv, pull in any dependencies, and fire the script.

  1. Upload the cloudflare-ddns folder to your home directory /home/your_username_here/

  2. Run the following code in terminal

crontab -e
  1. Add the following lines to sync your DNS records every 15 minutes
*/15 * * * * /home/your_username_here/cloudflare-ddns/start-sync.sh

Building from source

Create a config.json file with your production credentials.

πŸ’– Please Note

The optional docker-build-all.sh script requires Docker experimental support to be enabled.

Docker Hub has experimental support for multi-architecture builds. Their official blog post specifies easy instructions for building with Mac and Windows versions of Docker Desktop.

  1. Choose build platform
  • Multi-architecture (experimental) docker-build-all.sh

  • Linux/amd64 by default docker-build.sh

  1. Give your bash script permission to execute.
sudo chmod +x ./docker-build.sh
sudo chmod +x ./docker-build-all.sh
  1. At project root, run the docker-build.sh script.

Recommended for local development

./docker-build.sh

Recommended for production

./docker-build-all.sh

Run the locally compiled version

docker run -d timothyjmiller/cloudflare_ddns:latest

Supported Platforms

πŸ“œ Helpful links

License

This Template is licensed under the GNU General Public License, version 3 (GPLv3).

Author

Timothy Miller

View my GitHub profile πŸ’‘

View my personal website πŸ’»

cloudflare-ddns's People

Contributors

4n4n4s avatar adamantike avatar adamus1red avatar arpagon avatar bjackman avatar davide125 avatar dependabot[bot] avatar favonia avatar kernitus avatar luigifcruz avatar markormesher avatar merlinschumacher avatar nevah5 avatar omeganot avatar rojosinalma avatar suyun114 avatar timothymiller avatar wloot avatar xinxijishuwyq avatar zhanghan177 avatar zmilonas avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cloudflare-ddns's Issues

[Question] Does it make sense to get IPv6 address externally with privacy extensions enabled?

The idea about privacy extensions (RFC 4941) is that temporary addresses would be generated and become deprecated randomly for outbound connections and this is the address that is being picked up by https://[2606:4700:4700::1111]/cdn-cgi/trace. This means that there is a chance that after creating the AAAA record that address could be deprecated and any connections to that address would be refused.

From what I heard the best option would be to get the primary IPv6 address by parsing local commands like ifconfig instead.

What could you talk about this?

If subdomains option is empty, records never get updated

Describe the bug

If config.json contains the option

"subdomains": "",

then the code in the for loop in commitRecord effectively never gets executed (from line https://github.com/timothymiller/cloudflare-ddns/blob/master/cloudflare-ddns.py#L101).

I.e. lines 102 - 148 are never executed. Therefore, the DNS records are never updated.

To Reproduce
Steps to reproduce the behavior:

  1. Use config
{
  "cloudflare": [
    {
      "authentication": {
          "api_token": "xxx" 
      },
      "zone_id": "xxx",
      "subdomains": "",
      "proxied": false
    }
  ],
  "a": true,
  "aaaa": false,
  "purgeUnknownRecords": false
}
  1. Add a debug message immediately following the for subdomain in subdomains: statement
  2. start container and view logs
  3. debug message never gets displayed

Expected behavior

  • debug message should be displayed (proving that the code inside the for loop is running)
  • ultimately, the DNS record in cloudflare gets updated

Related

#73

IP Change causes duplicate DNS entry

When updating for an IP address change, it seems rather than replacing the IP on the current A record like DNSTube was doing, this is adding an A record with each IP so it's possible it can resolve to any of them.

image

Docker image not running the correct python version

Describe the bug
The docker image won't start and crashes with an error:

cloudflare-ddns     | Exception: 🐍 This script requires Python 3.5+
cloudflare-ddns     | Traceback (most recent call last):
cloudflare-ddns     |   File "/cloudflare-ddns.py", line 192, in <module>

To Reproduce
Take the latest image timothyjmiller/cloudflare-ddns:latest and start it.

Expected behavior
Should work like before the update.

Additional context
I guess it is related to this recent commit: bf61357

reduce unimportant logging?

Thanks for the ddns scripting. It worked the first time!

Please consider the verbosity of the current logging. At bottom is what I see on the second and subsequent runs, when no DNS change is made. (In this logging, the WARNING at bottom is on stderr, while the rest is on stdout.)

Instead, I'd like to see output which is relatively quiet if nothing is changed. This would help the current, nice output "Updating record" logging be noticeable when there is a change, or notice real errors when they might appear.

Can you control the pip version and the python libraries as part of the docker scripting? Can you reduce this logging which seems like just noise, and/or make the warning go away with an upgrade or other means?

Thanks for considering this, and providing this useful tool.

Larry

$ ./start-sync.sh
Requirement already satisfied: requests in ./venv/lib/python3.7/site-packages (2.25.1)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in ./venv/lib/python3.7/site-packages (from requests) (1.26.4)
Requirement already satisfied: idna<3,>=2.5 in ./venv/lib/python3.7/site-packages (from requests) (2.10)
Requirement already satisfied: certifi>=2017.4.17 in ./venv/lib/python3.7/site-packages (from requests) (2020.12.5)
Requirement already satisfied: chardet<5,>=3.0.2 in ./venv/lib/python3.7/site-packages (from requests) (4.0.0)
WARNING: You are using pip version 19.2.3, however version 21.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

Do i need to open ports?

does is need opened ports? or it has a dedicated server to redirect me? Its bedder if i do not need to open em because i cant do it all ways

Assumes Subdomains Exist

Describe the bug
Looking at Cloudflare.py, it seems it relies mostly on the existence of subdomains to update the records, including root records. I've added some basic logic I'll PR that allows people who only are using root domains to change config.json to:
"subdomains": [],

To Reproduce
Steps to reproduce the behavior:
Have subdomains as:

 "subdomains": [
        "",
       ],
  1. Run script, errors out with invalid DNS Record, along with the 'name' part of the request blank:
    {'type': 'A', 'name': , 'content': '1.1.1.1', 'proxied': False, 'ttl': 300}

Expected behavior
With no subdomains listed, I would assume the root domain be updated instead, but the logic did not exist.

Use Cloudflare Trace Instead of ipify or a self hosted solution

Is your feature request related to a problem? Please describe.
The current implementation checks your server to determine the project's public ipv4 address. If you go away, the request will fail.

Describe the solution you'd like
I think we should leverage clouflare's trace feature https://cloudflare.com/cdn-cgi/trace.
If cloudflare goes away, well so does the entire point of this script, and I suspect cloudflare trace will be a bit more reliable.
While making this change you could also migrate the ipv6 check from ipify to cloudflare trace as well.

Describe alternatives you've considered
To make it internally consistent, you could use ipify for both ipv4 and ipv6, but I think depending on a single vendor is a better idea.

Additional context
N/A

The root domain record is no longer updated (CF Error 9000: DNS name is invalid)

Describe the bug
As a result of the script execution, only subdomains are updated. When trying to update the root domain record, it gives an error 9000: "DNS name is invalid".
Until about the end of June, everything worked properly. Not now.

To Reproduce
Steps to reproduce the behavior:

  1. Edit the config, also add "" to subdomains.
  2. Run script

Expected behavior
The root domain record must also be updated.

Additional context
I tried to change the tokens and the authorization method. It still throws an error only on the root domain.
However, it seems I was able to solve the problem with a small edit of the script. I changed the value of the variable "name" from subdomain to fqdn. With these edits, everything works as it should again. I can assume that Cloudflare made minor edits to the API, and now an empty domain name is not accepted (previously, the root record was updated under this condition, as I understand it), and now you always need to specify the full one.

Wait 5 minutes for first update?

Starting the application I see:

βš™οΈ Individually disable IPv4 or IPv6 with new config.json options. Read more about it here: https://github.com/timothymiller/cloudflare-ddns/blob/master/README.md
πŸ•°οΈ Updating IPv4 (A) & IPv6 (AAAA) records every 5 minutes

Then after 5 minutes, I see the addition of:

🧩 IPv6 not detected
βž• Adding new record {'type': 'A', 'name': 'DOMAIN', 'content': 'IP_ADDRESS', 'proxied': False, 'ttl': 300}

I would expect an initial update on application start, then poll every 5 minutes. It is unexpected to wait 5 minutes for the first update.

Running with a standard config:

{
  "cloudflare": [
    {
      "authentication": {
          "api_token": "APITOKEN"
      },
      "zone_id": "ZONEID",
      "subdomains": [
        "DOMAIN"
      ],
      "proxied": false
    }
  ]
}

Configuration to enable updating of IPv6 or IPv4 only

Is your feature request related to a problem? Please describe.
I use a provider in Belgium that in their router I don't have option to configure port forwarding for IPv6 addresses. So when I have the AAAA record created, sometime I can connect with my NextCloud running in my Raspberry Pi and sometimes not.

Describe the solution you'd like
I propose to have a configuration to disable the update of IPv6 of IPv4 for a domain in config.json

Describe alternatives you've considered
As an alternative now I remove the like in docker-compose.yml that configure the network mode as host.

override not working

for every new IP Adresse it creates a new entry but the old entrys are still there
it is the same for IPv4 and IPv6

Custom interval for DNS updating

Is your feature request related to a problem? Please describe.
I love how simple and easy this image makes updating my DNS records on cloudflare, but I'd like to configure the frequency at which the DNS records are updated

Describe the solution you'd like
I'd like an option in the configuration file that allows a custom interval between DNS updates.

Describe alternatives you've considered
Manually editing the container to enable this feature is either very clunky or diverges from upstream very quickly. Orchestrating the container's lifecycle with another service would also be a solution, but right now the container does not run in a one-off context.

Ability to specify which resource records are updated per host

Right now if the docker image detects an ipv6 address it will automatically apply both the A and AAAA records. I'd like to have the ability to control the updates by specifying the record type that will get updated by specifying it inside the config.json.

Reduce or allow configuration of record TTL

Is your feature request related to a problem? Please describe.
Potentially, a problem could arise from a < 5 min gap in service availability between IP changes

Describe the solution you'd like
Lower record TTL to 1 minute or (preferably) allow configurable record TTL using the JSON config file

Describe alternatives you've considered
Manually changing TTL in cloudflare, but I believe the script will overwrite it each time

The current record TTL is hardcoded at 300 seconds, or 5 minutes. This means services could potentially be unavailable for up to 5 minutes between IP changes. I suggest a configuration option is added to allow configurable TTL, to support use cases where a higher or lower TTL would be more appropriate.

Issue with base_domain_name

Traceback (most recent call last): File "cloudflare-ddns.py", line 98, in <module> commitRecord(ip) File "cloudflare-ddns.py", line 41, in commitRecord base_domain_name = response["result"]["name"] TypeError: 'NoneType' object is not subscriptable

When running ./sync, this is the error that pops up. I have quite a few domainnames @ Cloudflare.

Thank you!

This is the perfect software for what I need right now.

Brief, accurate documentation - worked first time- so nice.

thanks!

Error: NO RESPONSE FROM FUNCTION

Describe the bug
I don't seem to be able to get the script to work anymore, upon running, for every host added it will print this message:

Updating record {'type': 'A', 'name': '', 'content': 'An error occurred with this application.\n\nNO_RESPONSE_FROM_FUNCTION\n', 'proxied': False}

To Reproduce
Configure config.json as usual. This issue happens with both api tokens and api keys.

Expected behavior
For it to update the DNS records.

Desktop:
Raspberry Pi 4

Docker image crashlooping / `This script requires Python 3.5+`

Describe the bug
Image crashlooping

 Traceback (most recent call last):                                                                                                                                                                                                       
   File "/cloudflare-ddns.py", line 192, in <module>                                                                                                                                                                                      
     raise Exception("🐍 This script requires Python 3.5+")                                                                                                                                                                               
 Exception: 🐍 This script requires Python 3.5+                                                                                                                                                                                           

To Reproduce
Update to latest docker hub image with digest: sha256:8bd682a383014dfea17c8e2d32bac59e3a1e5a0e767cb737469056cba5d4bbac

I can't tell for sure if the error just came with this release, or if it exists longer.

Expected behavior
Image running

Additional context
Using it in a k3s Kubernetes cluster.

Fatal Python error: init_interp_main: can't initialize time

Describe the bug
Without privileged: true on docker-compose.yml container just exits with

Attaching to cloudflare-ddns
cloudflare-ddns    | Fatal Python error: init_interp_main: can't initialize time
cloudflare-ddns    | Python runtime state: core initialized
cloudflare-ddns    | PermissionError: [Errno 1] Operation not permitted
cloudflare-ddns    | 
cloudflare-ddns    | Current thread 0x76fb7390 (most recent call first):
cloudflare-ddns    | <no Python frame>
cloudflare-ddns exited with code 1

To Reproduce
Steps to reproduce the behavior:

  1. Clone repository
  2. Setup docker-compose and config
services:
  cloudflare-ddns:
    image: timothyjmiller/cloudflare-ddns:latest
    container_name: cloudflare-ddns
    security_opt:
      - no-new-privileges:true
    network_mode: "host"
    environment:
      - PUID=1000
      - PGID=1000
    volumes:
      - ./config.json:/config.json
    restart: unless-stopped
{
  "cloudflare": [
    {
      "authentication": {
          "api_token": "<api_token>", 
          "api_key": {
              "api_key": "api_key_here",
              "account_email": "your_email_here"
                }
      },
      "zone_id": "<zone_id>",
      "subdomains": [
        ""
      ],
      "proxied": false
    }
  ],
  "a": true,
  "aaaa": true
}

  1. docker-compose up see error

System (please complete the following information):

  • OS: Raspberry Pi OS Lite January 11th 2021

Additional context
Running docker as pi user on Raspberry Pi 3

IPv4 Address retrieval doesn't work

Hear me out because you'll hate this. For me the IP address 1.1.1.1 doesn't work. All requests to it's ports per UDP or TCP just time out (ping and trace works thoo). This seems to be an issue with my ISP and I've no idea how to fix it.

On the bright side the cloudflare IPv6 and 1.0.0.1 alternative work perfectly. Would it be possible to configure secure the IP retrieval by adding the cloudflare fallback IPs?

start-sync.sh deleted all my A and AAAA records

This might be user error, but if this sort of destruction was way too easy to trigger.

I have a very simple config.json running at two sites.

{
  "cloudflare": [
    {
      "authentication": {
          "api_token": "***************************************************"
      },
      "zone_id": "********************************************",
      "subdomains": [
        "home"
      ],
      "proxied": false,
      "ttl": 120
    }
  ]
}

Site one (home) has no problems, and it works as expected, updating the single home subdomain A record. Site two (work) decided all my A and AAAA records were stale and must be destroyed. πŸ”₯

Again the only difference between the two configs was the subdomain was home vs work. The same API token and zone IDs were used, and the token is only for the specified zone.

Output from the great purge is as follows.

root@orangepione:~/cloudflare-ddns# ./start-sync.sh
Requirement already satisfied (use --upgrade to upgrade): requests in ./venv/lib/python3.5/site-packages
Requirement already satisfied (use --upgrade to upgrade): idna<3,>=2.5 in ./venv/lib/python3.5/site-packages (from requests)
Requirement already satisfied (use --upgrade to upgrade): urllib3<1.27,>=1.21.1 in ./venv/lib/python3.5/site-packages (from requests)
Requirement already satisfied (use --upgrade to upgrade): certifi>=2017.4.17 in ./venv/lib/python3.5/site-packages (from requests)
Requirement already satisfied (use --upgrade to upgrade): chardet<5,>=3.0.2 in ./venv/lib/python3.5/site-packages (from requests)
You are using pip version 8.1.1, however version 21.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
βš™οΈ Individually disable IPv4 or IPv6 with new config.json options. Read more about it here: https://github.com/timothymiller/cloudflare-ddns/blob/master/README.md
🧩 IPv4 not detected
πŸ—‘οΈ Deleted stale record 7824af031ca41433b6b2************
πŸ—‘οΈ Deleted stale record 1a27989afdb51c6754e2************
πŸ—‘οΈ Deleted stale record 8ba9e3afb5f1d77e3df8c************
πŸ—‘οΈ Deleted stale record 18b7a2e427866c66081252************
πŸ—‘οΈ Deleted stale record 278142ebceeaa2f24173957************
πŸ—‘οΈ Deleted stale record cc0c93ecb89a941f933f336************
πŸ—‘οΈ Deleted stale record 24a8a854f1f2195dd2abb4f************
πŸ—‘οΈ Deleted stale record 5ad62c21350fb73da7ee************
πŸ—‘οΈ Deleted stale record be1e9864eccfdc7c2e49e************
πŸ—‘οΈ Deleted stale record aa7eee7c35bfe7650d609************
πŸ—‘οΈ Deleted stale record b6dfa25f71065ec732bfbf************
🧩 IPv6 not detected
πŸ—‘οΈ Deleted stale record f281e92f333c99113a4f3************

Since then I moved the script and config to a Raspberry Pi 4, so I don't get the same errors above, but it still destroys any A or AAAA records I add.

Please help! How do I stop it from destroying my records!

And yes, the version that destroys my records was downloaded today. The one that works fine was downloaded a few months ago.

Feature Request: Ability to choose on a per subdomain basis the proxy state

Is your feature request related to a problem? Please describe.
I saw this was asked about a year ago via #30 but it wasn't in the feature request format. My hope is you'll reconsider adding this feature.

Describe the solution you'd like
I'd like to be able to specify whether a subdomain entry is proxied or not when updated.

Describe alternatives you've considered
I reviewed issue 30 and do not want to deploy Traefik at this time to handle a single subdomain in my case. I am going to setup a second instance of cloudflare-ddns per your other suggestion for the time being but it would be nice to only have to maintain one copy.

Additional context
I have a few subdomains where I need to leave proxying disabled for the service to function properly. I have a subdomain configured as a A Record that is a random string of characters and a few CNAMES pointing to it for the services that need a direct connection. An attacker could find them if they dug but for someone looking for low hanging fruit they'd probably pass by. I realize it's a security through obscurity thing but unfortunately its my only choice for these services for now.

IPv6 address (lan) required to update the WAN IPv6

Describe the bug
The script wont update AAAA unless the host running the script has a valid IPv6 address. If you're querying an external API to retrieve the address then you should be able to do this over IPv4

To Reproduce
Steps to reproduce the behavior:

  1. Setup & run the docker container on a bridge network
  2. WAN has valid IPv6
  3. Script says "IPv6 not detected"
  4. Run the container on the host network (host has valid ipv6) and it works

Expected behavior
I should be able to update the AAAA record based on the external IPv6 address regardless of the stack running the updater.

Error "cloudflare-ddns.py: error: argument --repeat: expected one argument"

Describe the bug
Starting the docker container yields the following error for me:

usage: cloudflare-ddns.py [-h] [--repeat REPEAT] [--config-path CONFIG]
cloudflare-ddns.py: error: argument --repeat: expected one argument

To Reproduce

  1. run docker container
  2. check logs

Additional context
Changing the containers command in a compose file to

command: ["python", "-u", "/cloudflare-ddns.py", "--repeat", "300"]

fixes the issue

Ability to choose on a per subdomain basis the proxy state

I've got some subdomains attached to my cloudflare account that are dynamically updated using this tool. I have a certain number of them proxied and some of them not for various reasons. Is it possible to have a feature where we can individually chose which subdomain is and isn't proxied or could the same be accomplished by running two of the same instances together with different configs? Any insight and replies are appreciated. Massive thank you from everyone here for making this great tool.

Ability to provide Cloudflare API token as a Docker secret

Is your feature request related to a problem? Please describe.
At the moment you have to provide your API token within the config file, meaning you can't commit that into any source control system.

Describe the solution you'd like
The ability to provide the token directly from Docker secrets. A good example in the wild is how Authelia handles this: https://www.authelia.com/docs/configuration/secrets.html. For every sensitive config key there is a corresponding environment variable that tells the service which file to read a value from.

Describe alternatives you've considered
You could treat the whole config file as a Docker secret, but that doesn't solve the source control problem. I also thought about a janky script to edit the config when the container starts up and insert the token, but that would be unstable and would only benefit me rather than all users.

Additional context
Happy to work on a PR for this if it would be a welcome contribution.

Help with "Error reading config.json" -- docker-compose

I'm unable to figure out why I keep getting this "error reading config.json" message no matter what I do. Running docker containers on my QNAP, below is my docker-compose .yml using a user-account without admin privileges:

version: "3.7"
services:
  cloudflare-ddns:
    image: timothyjmiller/cloudflare-ddns:latest
    container_name: cloudflare-ddns
    security_opt:
      - no-new-privileges:true
    environment:
      - PUID=1001
      - PGID=1001
    volumes:
      - /[path to app directory]/config.json:/config.json
    restart: always

I didn't use network_mode: "host" because I have no use for IPv6.

Below is my config.json file:

{
  "cloudflare": [
    {
      "authentication": {
          "api_token": "[my cloudflare token]",
      },
      "zone_id": "[DNS zone 1 ID]",
      "proxied": false
    },
    {
      "authentication": {
          "api_token": "[my cloudflare token]",
      },
      "zone_id": "[DNS zone 2 ID]",
      "proxied": false
    }
  ],
  "a": true,
  "aaaa": false,
  "purgeUnknownRecords": false
}

I'm only trying to update the IPs of two different root domains, so I removed the subdomain parts. I've checked and verified that my CF API token has the proper privileges to read zones and edit DNS.

I've tried both creating this config.json file using both the admin account that I use to SSH into my server, and using the non-admin account referred to in my docker-compose file. Both returns the same "error reading config.json" message.

What am I doing wrong?

Script creates new registries on existing entries when IP changes

Describe the bug
Using your script in my UMD Pro or using it standalone on a Ubuntu install, I see that anytime my public IP changes, the '@' records are not updated but a new entry are created for each new IP.

Also, in the logs I see the following logs info:

Updating A & AAAA records every 10 minutes
Adding new record {'type': 'A', 'name': ' ', 'content': 'publicIPv4', 'proxied': True, 'ttl': 120}
Adding new record {'type': 'AAAA', 'name': ' ', 'content': 'WAN port PublicIP', 'proxied': True, 'ttl': 120}

The config.json configuration follows your recommendations, using api_token option for authentication with edit permission in the specific DNS zone that I want to update.

The subdomain configuration is unique and it's for the root registration and it's set up with " " configuration. There are multiple subdomains in the zone that are set up with CNAME configuration.

The tests where done in UDM Pro podman container and also in Ubuntu.

To Reproduce
Steps to reproduce the behavior:

  1. Wait for a public IP to change
  2. Wait for cloudflare-dns to configure the changes
  3. Access Cloudflare admin console for the zone and see duplicated registries for IPv4 and/or IPv6

Expected behavior
Expecting the script to update existing A/AAAA configuration for the root domain and not to create new registries for the same configuration. When this happen and in specific conditions due to the DNS configuraiton, I lost access to the public IP. I need to get back to the admin console and delete the duplicated registries.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: Ubuntu Linux / UDM Pro
  • Browser N/A
  • Version 20.04 LTE

Duplicate subdomain entry when IP change.

This happened both ipv4 and ipv6
Steps to reproduce the behavior:

  1. Clone this repo
  2. Copy config-example.json to config.json
  3. Config in config.json to change IP at subdomain.domain.com, Remove the first "", line in the subdomain array.(Since I don't want to change IP of the domain.com)
  4. Run the script

Expected behavior
The script will detect old IP from subdomain.domain.com and change the IP in subdomain.domain.com record to the new one.

Actual behavior
The script creates another record for subdomain.domain.com.

Updating Base Domain

When placing an empty string or an '@' in the array of subdomains, the script creates a new entry for the base domain instead of updating the already existing one.
From the documentation, and issues #1 and #2, I gather it should be an empty string, however this configuration doesn't seem to work. In the following example, the www subdomain entry is updated, while a new one is created every time for the base domain.

"subdomains": [ "", "www" ]

Script not running with crontab

Describe the bug
Crontab not running script. Ubuntu 20.0.0.4

To Reproduce
Running ./start-sync.sh with crontab doesn't run python script with message error in screenshot.

Error: [Errno 13] Permission denied: '/home/andrew/venv/pyvenv.cfg'

ERROR: Could not open requirements file: [Errno 2] No such file or directory: 'r equirements.txt' Traceback (most recent call last): File "cloudflare-ddns.py", line 17, in

import requests

ModuleNotFoundError: No module named 'requests'

Expected behavior
Crontab to run script

Screenshots
If applicable, add screenshots to help explain your problem
Hi
I can't get crontab to run the script Ubuntu 20.0.4

Specify interface for updating

I am running the container on WSL and I noticed a potential issue. Right now WSL doesn't support IPv6 access, so the container reported "IPv6 not detected". On Windows, WSL creates a virtual ethernet device with a local v4 and v6 address, and I'll assume that local addresses are ignored for cloudflare-ddns.

But, the machine currently running cloudflare-ddns has two network interfaces, ethernet and wifi. I want my domain to point to the ethernet addresses. Since my machine is behind a router, the A record would just point to the public v4 address of the network, but for non-NAT networks and IPv6, it's unclear which address might be used for the records, or perhaps a record might be made for each valid IP address found. Providing an option to specify interfaces under each zone in config.json could fix this issue if it would ever come up.

error reading config.json

Describe the bug
I cloned the project, edited just the subdomains and the credentials, the docker logs as 😑 Error reading config.json

To Reproduce
Steps to reproduce the behavior:
Clone the project, edit subdomains and credentials.
run ./docker-build.sh
docker run -d {image id}

Expected behavior
would run normally

Screenshots
image
image
image

My config file :
image

Desktop (please complete the following information):
Edited on :

  • OS: Windows
  • Version 11
    Ran On :
  • OS: Ubuntu Server
  • Version 20.04

Additional context
Add any other context about the problem here.

deleteEntries function does not work correctly.

Describe the bug
Due to incorrect placement of the code block in the deleteEntries function it does not work correctly. It removes records only from the last domain zone in the list.

To Reproduce
Steps to reproduce the behavior:

  1. Edit the config, also enable the purgeUnknownRecords option and add two or more Cloudflare configs.
  2. Run script.

Expected behavior
Existing records from all zones must be deleted.

Max retries exceeded

Describe the bug
Retries on subsequent updates post installation are hitting some kind of retry limit, possibly due to "Development mode" enabled on Cloudflare (untested)

To Reproduce
Steps to reproduce the behavior:

  1. Install cloudflare-ddns docker on LXC (untested in VM or baremetal install)
  2. Run cloudflare-ddns docker
  3. wait 5 mins for update OR make adjustments to config file and save

Expected behavior
Should update just fine on subsequent requests

Screenshots
see log below

Desktop (please complete the following information):

  • OS: Docker on ArchLinux LXC in Proxmox

Additional context

Upon running the docker up, the ip is updated just fine. Subsequent updates have the following log output to stdout.

cloudflare-ddns  | Traceback (most recent call last):
cloudflare-ddns  |   File "/usr/local/lib/python3.10/site-packages/urllib3/connection.py", line 174, in _new_conn
cloudflare-ddns  |     conn = connection.create_connection(
cloudflare-ddns  |   File "/usr/local/lib/python3.10/site-packages/urllib3/util/connection.py", line 73, in create_connection
cloudflare-ddns  |     for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
cloudflare-ddns  |   File "/usr/local/lib/python3.10/socket.py", line 955, in getaddrinfo
cloudflare-ddns  |     for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
cloudflare-ddns  | socket.gaierror: [Errno -3] Try again
cloudflare-ddns  | 
cloudflare-ddns  | During handling of the above exception, another exception occurred:
cloudflare-ddns  | 
cloudflare-ddns  | Traceback (most recent call last):
cloudflare-ddns  |   File "/usr/local/lib/python3.10/site-packages/urllib3/connectionpool.py", line 699, in urlopen
cloudflare-ddns  |     httplib_response = self._make_request(
cloudflare-ddns  |   File "/usr/local/lib/python3.10/site-packages/urllib3/connectionpool.py", line 382, in _make_request
cloudflare-ddns  |     self._validate_conn(conn)
cloudflare-ddns  |   File "/usr/local/lib/python3.10/site-packages/urllib3/connectionpool.py", line 1010, in _validate_conn
cloudflare-ddns  |     conn.connect()
cloudflare-ddns  |   File "/usr/local/lib/python3.10/site-packages/urllib3/connection.py", line 358, in connect
cloudflare-ddns  |     conn = self._new_conn()
cloudflare-ddns  |   File "/usr/local/lib/python3.10/site-packages/urllib3/connection.py", line 186, in _new_conn
cloudflare-ddns  |     raise NewConnectionError(
cloudflare-ddns  | urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPSConnection object at 0x7f29c55f3c10>: Failed to establish a new connection: [Errno -3] Try again
cloudflare-ddns  | 
cloudflare-ddns  | During handling of the above exception, another exception occurred:
cloudflare-ddns  | 
cloudflare-ddns  | Traceback (most recent call last):
cloudflare-ddns  |   File "/usr/local/lib/python3.10/site-packages/requests/adapters.py", line 439, in send
cloudflare-ddns  |     resp = conn.urlopen(
cloudflare-ddns  |   File "/usr/local/lib/python3.10/site-packages/urllib3/connectionpool.py", line 755, in urlopen
cloudflare-ddns  |     retries = retries.increment(
cloudflare-ddns  |   File "/usr/local/lib/python3.10/site-packages/urllib3/util/retry.py", line 574, in increment
cloudflare-ddns  |     raise MaxRetryError(_pool, url, error or ResponseError(cause))
cloudflare-ddns  | urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='api.cloudflare.com', port=443): Max retries exceeded with url: /client/v4/zones/<MY ZONE> (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7f29c55f3c10>: Failed to establish a new connection: [Errno -3] Try again'))
cloudflare-ddns  | 
cloudflare-ddns  | During handling of the above exception, another exception occurred:
cloudflare-ddns  | 
cloudflare-ddns  | Traceback (most recent call last):
cloudflare-ddns  |   File "/cloudflare-ddns.py", line 227, in <module>
cloudflare-ddns  |     updateIPs(getIPs())
cloudflare-ddns  |   File "/cloudflare-ddns.py", line 181, in updateIPs
cloudflare-ddns  |     commitRecord(ip)
cloudflare-ddns  |   File "/cloudflare-ddns.py", line 95, in commitRecord
cloudflare-ddns  |     response = cf_api("zones/" + option['zone_id'], "GET", option)
cloudflare-ddns  |   File "/cloudflare-ddns.py", line 165, in cf_api
cloudflare-ddns  |     response = requests.request(
cloudflare-ddns  |   File "/usr/local/lib/python3.10/site-packages/requests/api.py", line 61, in request
cloudflare-ddns  |     return session.request(method=method, url=url, **kwargs)
cloudflare-ddns  |   File "/usr/local/lib/python3.10/site-packages/requests/sessions.py", line 542, in request
cloudflare-ddns  |     resp = self.send(prep, **send_kwargs)
cloudflare-ddns  |   File "/usr/local/lib/python3.10/site-packages/requests/sessions.py", line 655, in send
cloudflare-ddns  |     r = adapter.send(request, **kwargs)
cloudflare-ddns  |   File "/usr/local/lib/python3.10/site-packages/requests/adapters.py", line 516, in send
cloudflare-ddns  |     raise ConnectionError(e, request=request)
cloudflare-ddns  | requests.exceptions.ConnectionError: HTTPSConnectionPool(host='api.cloudflare.com', port=443): Max retries exceeded with url: /client/v4/zones/<MY ZONE> (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7f29c55f3c10>: Failed to establish a new connection: [Errno -3] Try again'))
cloudflare-ddns exited with code 1
cloudflare-ddns  | βš™οΈ Individually disable IPv4 or IPv6 with new config.json options. Read more about it here: https://github.com/timothymiller/cloudflare-ddns/blob/master/README.md
cloudflare-ddns  | βš™οΈ No config detected for 'purgeUnknownRecords' - defaulting to False
cloudflare-ddns  | πŸ•°οΈ Updating IPv4 (A) & IPv6 (AAAA) records every 5 minutes
cloudflare-ddns  | 🧩 IPv6 not detected

Script crashes due to concatenate error in line 155

Describe the bug
The script crashes in line 155 because it tries to concat a string and an int. I uses docker to run it.

cloudflare-ddns    | Traceback (most recent call last):
cloudflare-ddns    |   File "/cloudflare-ddns.py", line 155, in <module>
cloudflare-ddns    |     print("Updating A & AAAA records every " + delay + " seconds")
cloudflare-ddns    | TypeError: can only concatenate str (not "int") to str

To Reproduce
Steps to reproduce the behavior:
create a docker-compose setup.
run the container
the script won't run.

Expected behavior
The script to run as expected

Update Interval Support in config.json

Is your feature request related to a problem? Please describe.
It would be nice to be able to change the update times for the entry in the config.json file so for example the one I'm using I want it to update more often in the case of a WAN failover even than the default 10 min.
Describe the solution you'd like
Variable for the update interval in config.json

Docker Container constantly restarting, Error Code 1

I am following along the instructions for running this dynamic DNS client on my Raspberry Pi 4. I generated my own API Token in Cloudflare and made sure to test that it worked. I copied this token along with the Zone ID into the config.json file.

"cloudflare": [
    {
      "authentication": {
          "api_token": "redacted"
      },
      "zone_id": "redacted",
      "subdomains": [
        "",
        "my-subdomain"
      ],
      "proxied": false
    }
  ]
}

I also made sure that the path to the config.json file was mounted as a volume inside of the Docker.compose.yml file:

version: "3.7"
services:
  cloudflare-ddns:
    image: timothyjmiller/cloudflare-ddns:latest
    container_name: cloudflare-ddns
    security_opt:
      - no-new-privileges:true
    network_mode: "host"
    environment:
      - PUID=1000
      - PGID=1000
    volumes:
      - /pathtoconfig/cloudflare-ddns/config.json:/config.json
    restart: unless-stopped

I run docker-compose up -d, and at first it seems that the Docker container is working properly. However after a minute, when I check the status of the docker containers, I see that it has exited with error-code 1.

CONTAINER ID        IMAGE                                   COMMAND                  CREATED             STATUS                         PORTS                 NAMES
6fd340e2e47d        timothyjmiller/cloudflare-ddns:latest   "python -u /cloudfla…"   19 minutes ago      Restarting (1) 3 seconds ago                            cloudflare-ddns

I try and inspect the docker logs to see what went wrong:

docker logs -f cloudflare-ddns
standard_init_linux.go:211: exec user process caused "exec format error"

So I am somewhat stumped on this issue. I am running Raspbian 10 on a Raspberry Pi 4
If you have any advice on this problem or if there is something wrong with my configuartion please let me know.

Handle SIGTERM for graceful/faster shutdown

Is your feature request related to a problem? Please describe.
Right now the container won't stop until docker-compose sends a SIGKILL after waiting several seconds.

Describe the solution you'd like
The script should handle the SIGTERM signal that docker-compose sends first to shutdown gracefully, and faster.

Describe alternatives you've considered
N/A

Additional context
I may PR this change, depending on time.

all DNS records got deleted when IPv4 not detected

Describe the bug
Hi there, I had facing a problem where DDNS accidentally (randomly ) deleted all my DNS records on my site.
This happened three times on three different machines that installs ddns scripts.
I set up the config as in Doc:

{
  "cloudflare": [
    {
      "authentication": {
          "api_token": "xxx" 
      },
      "zone_id": "xxx",
      "subdomains": [
        "arm"
      ],
      "proxied": false
    }
  ],
  "a": true,
  "aaaa": false
}

To Reproduce
Crontab calling start-sync.sh every 4 mins
Expected behavior
should only update subdomain's ip address

Screenshots
the log output from the script:
image
it clear shows when IPv4 not detected, the program deleted all sub-domain dns record of my site.

here is the log in clouldflare:
image

Desktop (please complete the following information):

  • OS: [Ubuntu 20.04]

Smartphone (please complete the following information):
N/A

Additional context
N/A

Deployed with Linux, no errors, IP does not update

I deployed cloudflare ddns as it was described in the readme with linux and cron. It didn't show any errors and in cloudflare the status of my domain changed to just DNS. So I thought everything was up and running but it hasn't updated the IP address at all so far.
What issues could I check for?

Thank you in advance.

  • RaspberryPi 4 B
  • Raspbian 2021-03-04

Script sometimes deletes other A entrys

Describe the bug
The script sometimes deletes other A entries.

Might have something to do with the bug #71 because I get the same errors.

Server
Docker on proxmox ubuntu 18 lxc.

YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated

Describe the bug
PyYAML yaml.load(input) Deprecation

To Reproduce
Steps to reproduce the behavior:

  1. Go to

python3 cloudflare_ddns.py config.yaml

Expected behavior

Update the DNS records

Screenshots
image

Desktop (please complete the following information):

  • OS: Raspbian
  • Version 10

Container exits straight away

Since the last update the container exits straight after launch. The only logged entry is a python error:

Traceback (most recent call last): File "/cloudflare-ddns.py", line 248, in <module> print("πŸ•°οΈ Updating IPv4 (A) records every " + ttl + " seconds") TypeError: can only concatenate str (not "int") to str

Looks like the older images got deleted from dockerhub so reverting to a working setup by tagging the previous release is not possible.

Empty string for subdomains throwing error

Describe the bug

Following the documentation, which I now quote:

"subdomains": "Array of subdomains you want to update the A & where applicable, AAAA records. IMPORTANT! Only write subdomain name. Do not include the base domain name. (e.g. foo or an empty string to update the base domain)",

I have defined subdomains in config.json as follows:

      "subdomains": [
        ""
      ],

But I am seeing the following errors in the log:

πŸ•°οΈ Updating IPv4 (A) records every 5 minutes
Traceback (most recent call last):
  File "/cloudflare-ddns.py", line 227, in <module>
    updateIPs(getIPs())
  File "/cloudflare-ddns.py", line 181, in updateIPs
    commitRecord(ip)
  File "/cloudflare-ddns.py", line 94, in commitRecord
    subdomains = option["subdomains"]
KeyError: 'subdomains'

To Reproduce

Steps to reproduce the behavior:

  1. Set up config.json with subdomains defined as above
  2. Run the container in docker
  3. looks at the logs
  4. See error

Expected behavior

I guess, no error?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.