{"id":13955,"date":"2022-06-15T00:18:24","date_gmt":"2022-06-14T22:18:24","guid":{"rendered":"https:\/\/contabo.com\/blog\/?p=13955"},"modified":"2022-07-20T08:44:00","modified_gmt":"2022-07-20T06:44:00","slug":"the-ultimate-guide-to-docker-installation-and-deployment-using-cloud-init","status":"publish","type":"post","link":"https:\/\/contabo.com\/blog\/the-ultimate-guide-to-docker-installation-and-deployment-using-cloud-init\/","title":{"rendered":"The Ultimate Guide to Docker installation and deployment using Cloud-Init"},"content":{"rendered":"\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"630\" src=\"https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/blog-head_1200x630_docker-installation.jpg\" alt=\"blog_header\" class=\"wp-image-13958\" srcset=\"https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/blog-head_1200x630_docker-installation.jpg 1200w, https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/blog-head_1200x630_docker-installation-600x315.jpg 600w, https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/blog-head_1200x630_docker-installation-768x403.jpg 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n\n\n\n<p>Docker is an open platform to run your application in a loosely isolated environment and thereby separate it from the infrastructure it\u2019s running on. We all know the sentence \u201cBut it works on my machine\u201d and Docker tries to solve this problem. The environment your app is running on in Docker, called a Container, can easily be replicated on both the same host and other hosts or machines. On the machine you want the Container to run on, all you need to have installed is Docker itself. You can find more information about Containers and the differences to Virtual Machines in <a href=\"https:\/\/contabo.com\/blog\/containers-vs-virtual-machines\/\">this article<\/a>.<\/p>\n\n\n\n\n\n<h2 class=\"wp-block-heading\">Why should I use Docker?<\/h2>\n\n\n\n<p>There are many scenarios where Docker helps you to improve your workflows to consistently deliver your applications &#8211; fast and secure:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Development:<\/strong> When your developers code something locally and want to share their work, it might be complicated to replicate the exact same environments on the other machines (e.g. different operating system, required software is not installed, \u2026).But if the application is packaged in a standardized environment &#8211; in this case a Docker container &#8211; the other machine only needs to have Docker installed and the application can run on the other machine as well, because the environment gets replicated by Docker.<\/li><li><strong>Deployment:<\/strong> The part above also applies to the production environments: Whenever the software is fully tested and ready for deployment, all it takes is to upload the updated container.<\/li><li><strong>Portable:<\/strong> To run a container, only Docker is required. This means, you can run a container on the developer\u2019s local machine, a physical or virtual server in a data center, or on many cloud providers. No specific environment is required, because Docker will replicate the environments that\u2019s needed for the application to run.<\/li><\/ul>\n\n\n\n<p>To put it all together, Docker helps you to simplify development by creating the exact environment required for the app to run and thereby also makes the deployment of the app more reliable and consistent.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">And what is Cloud-Init, again?<\/h2>\n\n\n\n<p>You can imagine Cloud-Init as Docker but for cloud-instances rather than for applications. A Cloud-Init config describes the desired state of the instance and Cloud-Init will set up the instance exactly as described. Cloud-Init has many built-in modules to customize nearly every behavior of your instance, like setting up users and groups, adding SSH-Keys, installing software, or simply executing commands or creating files.<\/p>\n\n\n\n<p>But Cloud-Init is not just there to automate a single installation of a new VPS, instead it helps you set up a bunch of new instances at scale. You define the config once, and then use it multiple times to set up new instances. This can save you time that you may not have in the event of a disaster or when you need more instances to handle rapidly growing workloads.<\/p>\n\n\n\n<p>If you want to learn more about the fundamentals of Cloud-Init and why it\u2019s so cool, <a href=\"https:\/\/contabo.com\/blog\/what-is-cloud-init\/\">read our introduction to Cloud-Init here<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why should I use Docker with Cloud-Init?<\/h2>\n\n\n\n<p>Imagine your app is standardized in Docker, which helped you in the development process, and now it\u2019s time to deploy it. You don\u2019t have to care much about your app itself, because you created a Docker container and Docker will create the exact environment needed for your app to run. But before this can happen, Docker needs to be installed on your server. And to install Docker on your server, you first have to set it up, install all the required packages, maybe do some security stuff here, configure something there &#8211; it takes some time before you can actually deploy your app. And Cloud-Init helps you to automate this process by setting up the server (including the Docker installation) and then, when the server is ready, Docker can start your app. That\u2019s cool, isn\u2019t it? And although at Contabo you can get Docker preinstalled with the OS, there are reasons to use cloud-init instead,such as a more in-depth customization of the cloud instance. So let\u2019s get to work and create a Cloud-Init configuration file to help us with that.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Get started with Docker<\/h2>\n\n\n\n<p>To follow along, you need to have Docker installed on your system. Head over to the <a href=\"https:\/\/docs.docker.com\/engine\/install\/\" rel=\"nofollow\">Docker documentation<\/a> for installation options for each operating system.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Create a demo project<\/h3>\n\n\n\n<p>To keep it simple, we\u2019re creating a simple NodeJS app that uses express as the webserver to display a \u201cHello world\u201d message. Make sure you have NodeJS installed and run the following commands in your terminal:<\/p>\n\n\n\n<p><code>npm init -y<\/code><\/p>\n\n\n\n<p><code>npm install express<\/code><\/p>\n\n\n\n<p>This will set up a new node project and install express. Then create a index.js file, that\u2019s where we write the code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const express = require(\"express\");\nconst app = express();\n\n\/\/ Use PORT specified in the environment variables or use 3000 as fallback\nconst PORT = process.env.PORT || 3000;\n\n\/\/ Listen to incoming GET requests\napp.get(\"\/\", function(request, response) {\n    response.send(\"Hello from NodeJS in Docker!\");\n});\n\n\/\/ Start the app on the specifiec port\napp.listen(PORT, function() {\n    console.log(`App is running on http:\/\/localhost:${PORT}`);\n});\n<\/code><\/pre>\n\n\n\n<p>We can test it now by running node index.js, but let\u2019s add this as a command to the package.json:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"name\": \"docker-cloudinit-sample\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"start\": \"node index.js\"\n  },\n  \"keywords\": &#91;],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"express\": \"^4.18.1\"\n  }\n}\n<\/code><\/pre>\n\n\n\n<p>Now we can start our app with the following command:<\/p>\n\n\n\n<p><code>npm run start<\/code><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Dockerfile<\/h3>\n\n\n\n<p>We know how to run the app and what needs to be installed, but Docker doesn\u2019t. Docker reads this information from the Dockerfile, so let\u2019s create one (filename is Dockerfile). We start with an image, but instead of using something like Debian or Ubuntu where we have to install NodeJS ourselves, we can use the node image. In this image, NodeJS is already installed.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>FROM node:16<\/code><\/pre>\n\n\n\n<p>Next, we need a directory to work in:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>WORKDIR \/usr\/src\/app<\/code><\/pre>\n\n\n\n<p>Now keep in mind that Docker will cache the steps specified in the Dockerfile. So in the next step we only copy the package files (package.json and package-lock.json) and install the dependencies. This means, Docker will not re-install the dependencies everytime we change something in the sourcecode &#8211; this step will only be re-executed when the package files change.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>COPY package*.json .\/\nRUN npm install\n# Use this in production:\n# RUN npm ci --only=production\n<\/code><\/pre>\n\n\n\n<p>All that\u2019s left now is to copy the code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>COPY . .<\/code><\/code><\/pre>\n\n\n\n<p>We can now also specify the port the app should run on. Keep in mind that this port is the internal port inside the container. To access it from outside of the container, we can map it to another port that\u2019s accessible from outside later when creating the container. In this example, the NodeJS app uses port 80 inside the container.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ENV PORT=80<\/code><\/pre>\n\n\n\n<p>To start the app, we need to run the command we specified when creating the NodeJS app:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CMD npm run start<\/code><\/pre>\n\n\n\n<p>Here is the final Dockerfile:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>FROM node:16\nWORKDIR \/usr\/src\/app\nCOPY package*.json .\/\nRUN npm install\nCOPY . .\nENV PORT=80\nCMD npm run start\n<\/code><\/pre>\n\n\n\n<p>But keep in mind that Docker will install all the required dependencies. So we want to create a .dockerignore file to ignore the node_modules folder (add everything to this file that should be ignored by Docker, e.g. logs):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># .dockerignore\nnode_modules\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Creating the image<\/h3>\n\n\n\n<p>The image can be created using a single command:<\/p>\n\n\n\n<p><code>docker build . -t &lt;your username&gt;\/node-web-app<\/code><\/p>\n\n\n\n<p>Use . as second argument if you run the command in the app\u2019s directory or replace it with the path to the app. The -t flag allows you to specify a name for the image. We\u2019ll need this later when spinning up containers and pushing it to the registry.<\/p>\n\n\n\n<p>Notice: You may face an error when trying to use that image on the server because of the platform (linux\/amd64 and linux\/amd64\/v8). You can resolve this issue by specifying the platform in the build process (append &#8211;platform linux\/amd64 to the command).<\/p>\n\n\n\n<p>To test the image, we can spin up a new container using the image we\u2019ve just created:<\/p>\n\n\n\n<p><code>docker run -p 5000:80 -d &lt;your username&gt;\/node-web-app<\/code><\/p>\n\n\n\n<p>The -p flag allows us to map a port that\u2019s accessible from outside to a port inside the container. Remember when we set the port to 80 in the Dockerfile? 5000:80 tells Docker that we want to redirect the public port 5000 to the private port 80 inside the container. This means, when we request &lt;ip&gt;:5000, the request will be sent to this container and treated within the container as a request to port 80.<\/p>\n\n\n\n<p>If you open your browser on localhost:5000, you should be able to see \u201cHello from NodeJS in Docker!\u201d.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Image hosting<\/h3>\n\n\n\n<p>Before we can use the image on the production server, we need to host it somewhere. A popular space for that is the <a href=\"https:\/\/hub.docker.com\" rel=\"nofollow\">Docker Hub<\/a>. Here are already many ready-to-use Docker images hosted (for example the node image we used while creating the Dockerfile) and you can also upload your own images. Another option is to use other registries like <a href=\"https:\/\/aws.amazon.com\/ecr\/\" rel=\"nofollow\">AWS ECR<\/a>.<\/p>\n\n\n\n<p>For this guide, let\u2019s create a free account on Docker Hub and publish the image there. After you\u2019ve created your account, click on the \u201cRepositories\u201d tab and create a new one. One repository can contain many images (stored as tags).<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1620\" height=\"1138\" src=\"https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/docker_hub_image_hosting.png\" alt=\"docker_hub_image_hosting\" class=\"wp-image-13971\" srcset=\"https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/docker_hub_image_hosting.png 1620w, https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/docker_hub_image_hosting-600x421.png 600w, https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/docker_hub_image_hosting-768x539.png 768w, https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/docker_hub_image_hosting-1536x1079.png 1536w\" sizes=\"auto, (max-width: 1620px) 100vw, 1620px\" \/><\/figure>\n\n\n\n<p>We push images to the registry using the Docker CLI, just as we did to create the image. But before the push command is available, we have to login using docker login.<\/p>\n\n\n\n<p>In order to push the image, make sure it\u2019s named exactly like your repository. You can also add a tag here:<\/p>\n\n\n\n<p><code>docker tag &lt;your username&gt;\/node-web-app &lt;dockerhub repo name&gt;:&lt;tag&gt;<\/code><\/p>\n\n\n\n<p>The final command looks something like this:<\/p>\n\n\n\n<p><code>docker tag einlinuus\/node-demo-app einlinuus\/testing:node<\/code><\/p>\n\n\n\n<p>And this image can now be pushed to the Docker Hub:<\/p>\n\n\n\n<p><code>docker push &lt;dockerhub repo name&gt;:&lt;tag&gt;<\/code><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Deploying Docker using Cloud-Init<\/h2>\n\n\n\n<p>For this guide, we\u2019ll use the image we just uploaded to Docker Hub as example. If you skipped that part, use the following Docker image:<\/p>\n\n\n\n<p><a href=\"https:\/\/hub.docker.com\/repository\/docker\/einlinuus\/testing\" rel=\"nofollow\">einlinuus\/testing:node<\/a><\/p>\n\n\n\n<p>This is the exact image we created and uploaded in the \u201cGet started with Docker\u201d steps above.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Convenience script<\/h3>\n\n\n\n<p>Docker can be installed using different methods, but we\u2019ll start with the convenience script:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#cloud-config\nruncmd:\n  - curl -fsSL https:\/\/get.docker.com | sh\n  - docker pull einlinuus\/testing:node\n  - docker run -d -p 80:80 einlinuus\/testing:node\n<\/code><\/pre>\n\n\n\n<p>That\u2019s a pretty easy config since we\u2019re only using one module (runcmd) with 3 commands:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Download the script using cURL and execute it<\/li><li>Pull the Docker image from Docker Hub and save it locally<\/li><li>Run the downloaded image and map port 80 to port 80 in the container (so our app is accessible from the default HTTP port with no port specification required)<\/li><\/ol>\n\n\n\n<p>And that\u2019s basically all you need to install and set up Docker with Cloud-Init. But as stated in the <a href=\"https:\/\/docs.docker.com\/engine\/install\/debian\/#install-using-the-convenience-script\" rel=\"nofollow\">official Docker documentation<\/a>, the convenience script is not recommended in production environments. And since our goal is to make the server ready for production, let\u2019s also take a look at another installation method.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Docker repository<\/h3>\n\n\n\n<p>In order to install Docker using this method, we first need to add the Docker repository. After that, the required packages can be installed just like any other package:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#cloud-config\npackage_update: true\npackage_upgrade: true\npackages:\n  - apt-transport-https\n  - ca-certificates\n  - curl\n  - gnupg-agent\n  - software-properties-common\nruncmd:\n  - curl -fsSL https:\/\/download.docker.com\/linux\/debian\/gpg | apt-key add -\n  - add-apt-repository \"deb &#91;arch=$(dpkg --print-architecture)] https:\/\/download.docker.com\/linux\/debian $(lsb_release -cs) stable\"\n  - apt-get update -y\n  - apt-get install -y docker-ce docker-ce-cli containerd.io\n  - systemctl start docker\n  - systemctl enable docker\n  - docker pull einlinuus\/testing:node\n  - docker run -d -p 80:80 einlinuus\/testing:node\n\n<\/code><\/pre>\n\n\n\n<p>The script above works for Debian, make sure to replace \u201cdebian\u201d with \u201cubuntu\u201d if your server is running Ubuntu.<\/p>\n\n\n\n<p>Now this config may look way more complex than it actually is:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Update &amp; Upgrade the packages (using the default package manager)<\/li><li>Install the following packages: apt-transport-https, ca-certificates, curl, gnupg-agent, software-properties-common. These packages are required for either Docker itself or the installation process<\/li><li>Add the Docker repository to the apt-repository list<\/li><li>Update the repository list<\/li><li>Install the following packages: docker-ce, docker-ce-cli, containerd.io. This was not possible before (step 2) since the Docker repository was not added<\/li><li>Start and enable the Docker service<\/li><\/ol>\n\n\n\n<p>And the last two commands are similar to the last installation method: Download the image &amp; start a new container using that image.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Setting up Docker and instantly deploying a Container over and over again can be time consuming. With Cloud-Init this just takes a few seconds!<\/p>\n","protected":false},"author":57,"featured_media":13958,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"_uag_custom_page_level_css":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[18],"tags":[1472,181,254,1471,510,894,905],"ppma_author":[1525],"class_list":["post-13955","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials","tag-cloud-init-3","tag-contabo","tag-dedicated-server","tag-docker","tag-linux","tag-vps","tag-vserver"],"uagb_featured_image_src":{"full":["https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/blog-head_1200x630_docker-installation.jpg",1200,630,false],"thumbnail":["https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/blog-head_1200x630_docker-installation-150x150.jpg",150,150,true],"medium":["https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/blog-head_1200x630_docker-installation-600x315.jpg",600,315,true],"medium_large":["https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/blog-head_1200x630_docker-installation-768x403.jpg",768,403,true],"large":["https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/blog-head_1200x630_docker-installation.jpg",1200,630,false],"1536x1536":["https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/blog-head_1200x630_docker-installation.jpg",1200,630,false],"2048x2048":["https:\/\/contabo.com\/blog\/wp-content\/uploads\/2022\/06\/blog-head_1200x630_docker-installation.jpg",1200,630,false]},"uagb_author_info":{"display_name":"Linus Benkner","author_link":"https:\/\/contabo.com\/blog\/author\/linus\/"},"uagb_comment_info":0,"uagb_excerpt":"Setting up Docker and instantly deploying a Container over and over again can be time consuming. With Cloud-Init this just takes a few seconds!","authors":[{"term_id":1525,"user_id":57,"is_guest":0,"slug":"linus","display_name":"Linus Benkner","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/396885b0c71ca364e267b5804deaef19e48538c136b5287377b0d481091abd10?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/contabo.com\/blog\/wp-json\/wp\/v2\/posts\/13955","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/contabo.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/contabo.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/contabo.com\/blog\/wp-json\/wp\/v2\/users\/57"}],"replies":[{"embeddable":true,"href":"https:\/\/contabo.com\/blog\/wp-json\/wp\/v2\/comments?post=13955"}],"version-history":[{"count":0,"href":"https:\/\/contabo.com\/blog\/wp-json\/wp\/v2\/posts\/13955\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/contabo.com\/blog\/wp-json\/wp\/v2\/media\/13958"}],"wp:attachment":[{"href":"https:\/\/contabo.com\/blog\/wp-json\/wp\/v2\/media?parent=13955"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/contabo.com\/blog\/wp-json\/wp\/v2\/categories?post=13955"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/contabo.com\/blog\/wp-json\/wp\/v2\/tags?post=13955"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/contabo.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=13955"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}