{
    "componentChunkName": "component---src-templates-blog-post-tsx",
    "path": "/blog/2022-01-24-backstage-with-vscode/",
    "result": {"data":{"blogPost":{"title":"Tutorial: Easier onboarding with Backstage and VSCode dev containers","slug":"/blog/2022-01-24-backstage-with-vscode/","authorNodes":[{"name":"Min Kim","slug":"/people/min-kim/"}],"markdown":{"html":"<p>An often underrated aspect of developer onboarding is how quickly a new contributor can go from cloning the source code to running the project on their machine. An onboarding experience full of friction can be discouraging and leave a poor impression, while a smooth one is valuable for new and seasoned contributors alike.</p>\n<p>However, onboarding can be especially challenging for <a href=\"https://backstage.io/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Backstage</a> because it is a full-stack application. Developing a Backstage-based Developer Portal requires having a recent version of Node.js, Yarn, and PostgreSQL running on the developer's machine. These are relatively easy to install on macOS and Linux but can be challenging on Windows, especially for someone new to the Node.js ecosystem.</p>\n<p>At Frontside, we've had success improving onboarding by using <a href=\"https://code.visualstudio.com/docs/remote/containers\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">dev containers</a>, which spins up a docker-based local environment with all the dependencies that a project needs.</p>\n<p>Dev containers has a CLI called <a href=\"https://code.visualstudio.com/docs/remote/devcontainer-cli\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">devcontainer-cli</a> you can use to open the container from the command line. It will open in VSCode, which uses <a href=\"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Remote Containers Extension</a> to create a smooth integration.</p>\n<p>For the most part, running dev containers inside VSCode feels like the application is running on your machine. Once you've added dev containers to your project, there’s a clear pathway to adopting GitHub Codespaces, which <a href=\"https://github.blog/2021-08-11-githubs-engineering-team-moved-codespaces/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">makes onboarding even easier</a>.</p>\n<p>This blog post will explain setting up dev containers for Backstage projects as a first step to create a streamlined onboarding for developers.</p>\n<h2 id=\"requirements\" style=\"position:relative;\"><a href=\"#requirements\" aria-label=\"requirements permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Requirements</h2>\n<p>Before you get started, make sure you have the following:</p>\n<ul>\n<li><a href=\"https://code.visualstudio.com/Download\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Visual Studio Code</a></li>\n<li><a href=\"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Remote Containers Extension for VSCode</a></li>\n<li><a href=\"https://www.docker.com/products/docker-desktop\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Docker Desktop</a></li>\n<li><a href=\"https://docs.docker.com/compose/install/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Docker Compose</a> (if you're running Linux)</li>\n</ul>\n<h2 id=\"create-a-backstage-app\" style=\"position:relative;\"><a href=\"#create-a-backstage-app\" aria-label=\"create a backstage app permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Create a Backstage App</h2>\n<p>To spin up a dev container, you first need a project to work from. You can instantiate a Backstage app using <code class=\"language-text\">@backstage/create-app</code>:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">npx @backstage/create-app</code></pre></div>\n<p>For this tutorial, you'll be using PostgreSQL:</p>\n<p><figure class=\"figure\"><img src=\"/img/2022-01-24-backstage-devcontainer/backstage-create-app.png\"><figcaption class=\"figure-caption\">backstage-create-app</figcaption></figure></p>\n<p>Once the app is done installing, open the new app's workspace in visual studio code and set up a dev container.</p>\n<h2 id=\"opening-a-project-inside-a-dev-container\" style=\"position:relative;\"><a href=\"#opening-a-project-inside-a-dev-container\" aria-label=\"opening a project inside a dev container permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Opening a Project inside a dev container</h2>\n<p>If you have the <a href=\"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"><code class=\"language-text\">Remote Containers</code></a> extension installed, you should be able to access its commands. Press <code class=\"language-text\">F1</code> and select <code class=\"language-text\">Remote-Containers: Open Folder in Container...</code>:</p>\n<p><figure class=\"figure\"><img src=\"/img/2022-01-24-backstage-devcontainer/remote-containers-open.png\"><figcaption class=\"figure-caption\">open-folder-container</figcaption></figure></p>\n<p>VSCode will detect that you do not have dev container configurations yet and prompt you to select a predefined container. You can go ahead and select any of them, as you'll be replacing all of those configurations later.</p>\n<p>Once you make your selection, VSCode will create configuration files in the <code class=\"language-text\">.devcontainer</code> directory, build the container according to those files, and launch your workspace in the container. (You can go to your Docker Desktop dashboard to confirm that the new container is running.)</p>\n<p>If you change your dev container configuration, you can run <code class=\"language-text\">Remote-Containers: Rebuild Container</code>. And when you are ready to exit out of your dev container, run <code class=\"language-text\">Remote-Containers: Reopen Folder Locally</code>.</p>\n<p>Now that you know how to launch and exit out of dev containers, you need to add configurations to run Backstage.</p>\n<h2 id=\"docker-compose---postgres\" style=\"position:relative;\"><a href=\"#docker-compose---postgres\" aria-label=\"docker compose   postgres permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Docker Compose - Postgres</h2>\n<p>If you try to start up Backstage now, you'll get an error in the terminal about there being no database to connect to. This is because you're not running an instance of Postgres yet, so you should start there. At the root of your project workspace create a <code class=\"language-text\">docker-compose.yaml</code> file:</p>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">version</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"3.8\"</span>\n\n<span class=\"token key atrule\">services</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">my_postgres_db</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">image</span><span class=\"token punctuation\">:</span> postgres\n    <span class=\"token key atrule\">environment</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">POSTGRES_USER</span><span class=\"token punctuation\">:</span> postgres_username\n      <span class=\"token key atrule\">POSTGRES_PASSWORD</span><span class=\"token punctuation\">:</span> postgres_password</code></pre></div>\n<p>You're creating a <code class=\"language-text\">my_postgres_db</code> service with the official <a href=\"https://hub.docker.com/_/postgres\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">postgres docker image</a> and providing it a username and password to bootstrap a Postgres account for local development.</p>\n<h2 id=\"docker-compose---dev-container\" style=\"position:relative;\"><a href=\"#docker-compose---dev-container\" aria-label=\"docker compose   dev container permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Docker Compose - dev container </h2>\n<p>Next, create a service for your dev container (this will replace the one that VSCode automatically generated earlier).</p>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">services</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">my_postgres_db</span><span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">...</span>\n  <span class=\"token key atrule\">my_devcontainer</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">image</span><span class=\"token punctuation\">:</span> mcr.microsoft.com/vscode/devcontainers/typescript<span class=\"token punctuation\">-</span>node<span class=\"token punctuation\">:</span><span class=\"token number\">14</span>\n    <span class=\"token key atrule\">command</span><span class=\"token punctuation\">:</span> /bin/sh <span class=\"token punctuation\">-</span>c \"while sleep 1000; do<span class=\"token punctuation\">:</span>; done\"\n    <span class=\"token key atrule\">volumes</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> .<span class=\"token punctuation\">:</span>/workspace\n    <span class=\"token key atrule\">environment</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">POSTGRES_HOST</span><span class=\"token punctuation\">:</span> my_postgres_db\n      <span class=\"token key atrule\">POSTGRES_USER</span><span class=\"token punctuation\">:</span> postgres_username\n      <span class=\"token key atrule\">POSTGRES_PASSWORD</span><span class=\"token punctuation\">:</span> postgres_password</code></pre></div>\n<p>For your dev container image, you’re using one of the docker images <a href=\"https://hub.docker.com/_/microsoft-vscode-devcontainers\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">created by Microsoft</a> and running a shell command to prevent the container from exiting on its own as suggested in the <a href=\"https://code.visualstudio.com/docs/remote/create-dev-container#_use-docker-compose\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">devcontainer docs</a>.</p>\n<p>For a container to have all of the files of the backstage app, you need to add <code class=\"language-text\">volumes: .:/workspace</code> as shown in the previous snippet. This will take the current directory (relative to the docker-compose file) and copy its contents to your container's <code class=\"language-text\">/workspace</code> directory.</p>\n<p>You're also passing in environment variables to access the local instance of Postgres. These should correspond to the values you specified when creating the <code class=\"language-text\">my_postgres_db</code> service. (Alternatively, you could provide these values as environment variables and pass them through the start script of your backstage backend.)</p>\n<h2 id=\"rebuild-devcontainer\" style=\"position:relative;\"><a href=\"#rebuild-devcontainer\" aria-label=\"rebuild devcontainer permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Rebuild <code class=\"language-text\">devcontainer</code></h2>\n<p>Now you need to update your dev container configurations to use the docker-compose file you created:</p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token comment\">// .devcontainers/devcontainer.json</span>\n<span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"dockerComposeFile\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"../docker-compose.yaml\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"service\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"my_devcontainer\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"workspaceFolder\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/workspace\"</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>For these changes to be applied, you will need to rebuild your dev container by running either <code class=\"language-text\">Remote-Containers: Rebuild Container</code> or <code class=\"language-text\">Rebuild and Reopen in Container</code>, depending on if you returned to your local environment or if you're still in the first dev container you launched. You can go ahead and delete the other files that were created inside <code class=\"language-text\">.devcontainer/</code>.</p>\n<h2 id=\"using-persistent-volumes\" style=\"position:relative;\"><a href=\"#using-persistent-volumes\" aria-label=\"using persistent volumes permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Using Persistent Volumes</h2>\n<p>At this point, if you try to add a dependency or run <code class=\"language-text\">yarn install</code>, you may notice that it takes much longer than it usually does in your local environment. This is because when you create files in a docker container, it writes to the container's writable layer, which is not very fast. The standard practice (as suggested by <a href=\"https://docs.docker.com/storage/volumes/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">docker docs</a>) is to use volumes.</p>\n<p>In your docker-compose file, add the three node_modules directories of your backstage app:</p>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">services</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">my_postgres_db</span><span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">...</span>\n  <span class=\"token key atrule\">my_devcontainer</span><span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">...</span>\n    <span class=\"token key atrule\">volumes</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> .<span class=\"token punctuation\">:</span>/workspace\n      <span class=\"token punctuation\">-</span> /workspace/node_modules\n      <span class=\"token punctuation\">-</span> /workspace/packages/app/node_modules\n      <span class=\"token punctuation\">-</span> /workspace/packages/backend/node_modules</code></pre></div>\n<p>This syntax can be a little confusing at first, and you may be wondering if and why <code class=\"language-text\">.:/workspace</code> omits <code class=\"language-text\">node_modules</code>. Although they are nested under the same <code class=\"language-text\">volumes</code> property, what they achieve is different. When you specify both a source and target path, docker-compose will treat that as a bind mount. But when you pass in only a target path, docker-compose will create an anonymous volume.</p>\n<p>What's happening here is docker-compose first copies your backstage app to the <code class=\"language-text\">/workspace</code> directory of your dev container and then maps out the contents of node_modules to persistent volumes. You can read more about the differences between a bind mount and a volume <a href=\"https://docs.docker.com/storage/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">here</a>.</p>\n<p>Once you complete these steps and rebuild and reopen your dev container, you should see a significant improvement when you run <code class=\"language-text\">yarn install</code>.</p>\n<h2 id=\"dev-containers-are-a-first-step\" style=\"position:relative;\"><a href=\"#dev-containers-are-a-first-step\" aria-label=\"dev containers are a first step permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Dev containers are a first step</h2>\n<p>Now that you know how to set up dev containers optimized for Backstage development, you can make it easier for your teammates to contribute to your Developer Portal (regardless of whether they use macOS, Linux or Windows) by including the settings explained in this tutorial in your repository.</p>\n<p>The setup we explained in this tutorial also allows people to opt-in (or not) to running dev containers, so these changes do not affect anybody with other local development preferences.</p>\n<p>Dev containers are meant to lower barriers for those who cannot run the project locally. However, it's important to point out that even though dev containers are a convenient way to run the project, the DX of developing in a dev container is still inferior in some ways to running the project locally because it introduces the overhead of running Docker containers. With that said, we still believe that they are a handy tool and will drive the DX of your project in the right direction.</p>","frontmatter":{"date":"January 24, 2022","description":"In this tutorial, you'll learn how to set up dev containers to make it easier to onboard developers.","tags":["backstage"],"img":{"childImageSharp":{"fixed":{"src":"/static/69c0454f06a75c884b68d6358a0058af/31987/2022-backstage-dev-containers-windows.png"}}}}}}},"pageContext":{"id":"c826c44c-ed6e-55cb-8f00-5b39ccd110bd"}},
    "staticQueryHashes": ["1241260443"]}