Files
jcreek.github.io/web-dev/dev-environment-container-vscode/index.html
T

1753 lines
66 KiB
HTML

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="canonical" href="https://www.jcreek.co.uk/web-dev/dev-environment-container-vscode/">
<link rel="prev" href="../../home-server/truenas-scale/mounting-smb-shares-at-boot-on-truenas-scale/">
<link rel="next" href="../simple-vue-spa-in-asp-dotnet-core/">
<link rel="icon" href="../../img/favicon.ico">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.6.12">
<title>Setting up remote containers for IAC dev environments in dotnet 6 with postgres and SMTP - Josh Creek's Blog</title>
<link rel="stylesheet" href="../../assets/stylesheets/main.2afb09e1.min.css">
<link rel="stylesheet" href="../../assets/stylesheets/palette.06af60db.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
<script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
<script id="__analytics">function __md_analytics(){function e(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],e("js",new Date),e("config","G-MYNBTM1YQW"),document.addEventListener("DOMContentLoaded",(function(){document.forms.search&&document.forms.search.query.addEventListener("blur",(function(){this.value&&e("event","search",{search_term:this.value})}));document$.subscribe((function(){var t=document.forms.feedback;if(void 0!==t)for(var a of t.querySelectorAll("[type=submit]"))a.addEventListener("click",(function(a){a.preventDefault();var n=document.location.pathname,d=this.getAttribute("data-md-value");e("event","feedback",{page:n,data:d}),t.firstElementChild.disabled=!0;var r=t.querySelector(".md-feedback__note [data-md-value='"+d+"']");r&&(r.hidden=!1)})),t.hidden=!1})),location$.subscribe((function(t){e("config","G-MYNBTM1YQW",{page_path:t.pathname})}))}));var t=document.createElement("script");t.async=!0,t.src="https://www.googletagmanager.com/gtag/js?id=G-MYNBTM1YQW",document.getElementById("__analytics").insertAdjacentElement("afterEnd",t)}</script>
<script>if("undefined"!=typeof __md_analytics){var consent=__md_get("__consent");consent&&consent.analytics&&__md_analytics()}</script>
</head>
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="amber" data-md-color-accent="amber">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#setting-up-remote-containers-for-iac-dev-environments-in-dotnet-6-with-postgres-and-smtp" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../.." title="Josh Creek&#39;s Blog" class="md-header__button md-logo" aria-label="Josh Creek's Blog" data-md-component="logo">
<img src="../../img/noogler-hat-edited-round.png" alt="logo">
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
Josh Creek's Blog
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
Setting up remote containers for IAC dev environments in dotnet 6 with postgres and SMTP
</span>
</div>
</div>
</div>
<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme="default" data-md-color-primary="amber" data-md-color-accent="amber" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_0">
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_1" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg>
</label>
<input class="md-option" data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme="slate" data-md-color-primary="amber" data-md-color-accent="amber" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_0" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg>
</label>
</form>
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"/></svg>
</a>
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
<div class="md-header__source">
<a href="https://github.com/jcreek/jcreek.github.io" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8M97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</div>
<div class="md-source__repository">
Spotted an issue?
</div>
</a>
</div>
</nav>
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
<div class="md-grid">
<ul class="md-tabs__list">
<li class="md-tabs__item">
<a href="../.." class="md-tabs__link">
Home
</a>
</li>
<li class="md-tabs__item">
<a href="../../deploying/docker-compose-for-asp-net-core-with-postgres-and-s3-backups/" class="md-tabs__link">
Containers & Deploying
</a>
</li>
<li class="md-tabs__item">
<a href="../../dev-team/git/" class="md-tabs__link">
Development Teams
</a>
</li>
<li class="md-tabs__item">
<a href="../../home-server/generative-ai/ollama/" class="md-tabs__link">
Home Server
</a>
</li>
<li class="md-tabs__item md-tabs__item--active">
<a href="./" class="md-tabs__link">
Web Development
</a>
</li>
</ul>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--lifted md-nav--integrated" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../.." title="Josh Creek&#39;s Blog" class="md-nav__button md-logo" aria-label="Josh Creek's Blog" data-md-component="logo">
<img src="../../img/noogler-hat-edited-round.png" alt="logo">
</a>
Josh Creek's Blog
</label>
<div class="md-nav__source">
<a href="https://github.com/jcreek/jcreek.github.io" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8M97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</div>
<div class="md-source__repository">
Spotted an issue?
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../.." class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2" >
<label class="md-nav__link" for="__nav_2" id="__nav_2_label" tabindex="0">
<span class="md-ellipsis">
Containers & Deploying
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2">
<span class="md-nav__icon md-icon"></span>
Containers & Deploying
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../deploying/docker-compose-for-asp-net-core-with-postgres-and-s3-backups/" class="md-nav__link">
<span class="md-ellipsis">
Docker Compose for ASP.Net Core with Postgres + S3 backups
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../deploying/docker-compose-s3-postgres-backup-and-restore/" class="md-nav__link">
<span class="md-ellipsis">
Docker Compose for S3 Backup and Restore of PostgreSQL
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../deploying/elasticsearch-and-kibana/" class="md-nav__link">
<span class="md-ellipsis">
Docker Compose for Elasticsearch and Kibana 7.9
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3" >
<label class="md-nav__link" for="__nav_3" id="__nav_3_label" tabindex="0">
<span class="md-ellipsis">
Development Teams
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_3">
<span class="md-nav__icon md-icon"></span>
Development Teams
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../dev-team/git/" class="md-nav__link">
<span class="md-ellipsis">
The way I recommend using git in a dev team
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../dev-team/readme-files/" class="md-nav__link">
<span class="md-ellipsis">
How to write an excellent readme file
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4" >
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="0">
<span class="md-ellipsis">
Home Server
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4">
<span class="md-nav__icon md-icon"></span>
Home Server
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4_1" >
<label class="md-nav__link" for="__nav_4_1" id="__nav_4_1_label" tabindex="0">
<span class="md-ellipsis">
Generative AI
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_1_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_1">
<span class="md-nav__icon md-icon"></span>
Generative AI
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../home-server/generative-ai/ollama/" class="md-nav__link">
<span class="md-ellipsis">
How to install Ollama with a ChatGPT-like web UI
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4_2" >
<label class="md-nav__link" for="__nav_4_2" id="__nav_4_2_label" tabindex="0">
<span class="md-ellipsis">
TrueNAS Scale
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_2">
<span class="md-nav__icon md-icon"></span>
TrueNAS Scale
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../home-server/truenas-scale/installing-homeassistant-on-truenas-scale/" class="md-nav__link">
<span class="md-ellipsis">
Installing Home Assistant OS on Truenas Scale
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../home-server/truenas-scale/mounting-smb-shares-at-boot-on-truenas-scale/" class="md-nav__link">
<span class="md-ellipsis">
Mounting SMB shares at boot on Truenas Scale
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5" checked>
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="">
<span class="md-ellipsis">
Web Development
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_5">
<span class="md-nav__icon md-icon"></span>
Web Development
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
Setting up remote containers for IAC dev environments in dotnet 6 with postgres and SMTP
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Setting up remote containers for IAC dev environments in dotnet 6 with postgres and SMTP
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#how-to-set-up-the-project" class="md-nav__link">
<span class="md-ellipsis">
How to set up the project
</span>
</a>
<nav class="md-nav" aria-label="How to set up the project">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#devcontainerjson" class="md-nav__link">
<span class="md-ellipsis">
devcontainer.json
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#docker-composeyml" class="md-nav__link">
<span class="md-ellipsis">
docker-compose.yml
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#dockerfile" class="md-nav__link">
<span class="md-ellipsis">
Dockerfile
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#appsettingsjson-and-appsettingsdevelopmentjson" class="md-nav__link">
<span class="md-ellipsis">
appsettings.json and appsettings.Development.json
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../simple-vue-spa-in-asp-dotnet-core/" class="md-nav__link">
<span class="md-ellipsis">
Creating a reactive SPA simply within an ASP.Net Core web app with Vue.js
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_3" >
<label class="md-nav__link" for="__nav_5_3" id="__nav_5_3_label" tabindex="">
<span class="md-ellipsis">
Dotnet & C#
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5_3">
<span class="md-nav__icon md-icon"></span>
Dotnet & C#
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../dotnet-csharp/pattern-matching/" class="md-nav__link">
<span class="md-ellipsis">
C# 8 Pattern Matching
</span>
</a>
</li>
<li class="md-nav__item">
<a href="/web-dev/dotnet-csharp/semantic-kernel-vector-database/index.html" class="md-nav__link">
<span class="md-ellipsis">
Semantic Kernel (GPT with C#) - using a vector database to store embeddings
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../dotnet-csharp/jsnlog-asp-net-6/" class="md-nav__link">
<span class="md-ellipsis">
Adding JSNLog to ASP .Net 6 with Serilog
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_4" >
<label class="md-nav__link" for="__nav_5_4" id="__nav_5_4_label" tabindex="">
<span class="md-ellipsis">
Javascript & Typescript
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5_4">
<span class="md-nav__icon md-icon"></span>
Javascript & Typescript
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../js/node-js-typescript/" class="md-nav__link">
<span class="md-ellipsis">
How to set up a Node.js Typescript project well
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../js/node-js-web-typescript/" class="md-nav__link">
<span class="md-ellipsis">
How to set up a Node.js Typescript web project well
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<nav class="md-tags" >
<span class="md-tag">asp</span>
<span class="md-tag">c#</span>
<span class="md-tag">dev environment</span>
<span class="md-tag">devcontainer</span>
<span class="md-tag">dotnet</span>
<span class="md-tag">iac</span>
<span class="md-tag">postgres</span>
<span class="md-tag">remote-container</span>
<span class="md-tag">smtp</span>
<span class="md-tag">vscode</span>
<span class="md-tag">web development</span>
</nav>
<h1 id="setting-up-remote-containers-for-iac-dev-environments-in-dotnet-6-with-postgres-and-smtp">Setting up remote containers for IAC dev environments in dotnet 6 with postgres and SMTP</h1>
<p><em>2022-04-20</em></p>
<p><img alt="" src="/img/devcontainer.gif" /></p>
<p>In this post I will go through how to set up remote containers for a dotnet 6 API project using PostgreSQL and an SMTP server, so that any new devs picking up the project only have to clone down the git repo and open it in Visual Studio code to get started debugging it.</p>
<p>No dependencies need to be installed onto developer machines for each project they work on, other than:</p>
<ul>
<li>Docker</li>
<li>Visual Studio Code</li>
</ul>
<p>Yes, that's all they need. They don't even need to install any extensions - the only one they need will be prompted the first time they open the project in vscode. Any extensions they need for the project will be loaded in a vscode server instance within the docker container, and then their local vscode will allow access to the vscode server as if they're using it natively, right down to providing terminal access within the container.</p>
<h2 id="how-to-set-up-the-project">How to set up the project</h2>
<p>You will need to ensure that you've created a <code>.gitattributes</code> file in the root folder if you don't have one already, and specified <code>* text=auto eol=lf</code> to default all the line endings to LF, if you're on Windows and find that swapping between the host and the container changes the line endings in all your files. Alternatively, if you're already using Windows line endings, use <code>crlf</code> instead of <code>lf</code>.</p>
<p>In the root of the repo create a folder called <code>.devcontainer</code>. Within this you need to create three files:</p>
<ol>
<li><code>devcontainer.json</code></li>
<li><code>docker-compose.yml</code></li>
<li><code>Dockerfile</code></li>
</ol>
<h3 id="devcontainerjson"><code>devcontainer.json</code></h3>
<p>This is customised from the default Microsoft file. You can change the name, the vscode settings (although if you're working with newer dotnet projects I'd recommend leaving <code>omnisharp.useModernNet</code> set to <code>true</code>), add any extensions you want to use in vscode for this project (find the ID for an extension by installing it in your local vscode, then right clicking it and clicking <code>Copy Extension ID</code> - I've included some of my favourites for aspdotnet projects but the only one you <em>need</em> is <code>ms-dotnettools.csharp</code>), the post-create commands and the remote user.</p>
<p>There is an issue where Omnisharp, the underlying technology in the C# vscode plugin doesn't work properly unless <code>dotnet restore</code> is run at the point of setting up the dev container, so do not remove that command.</p>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-1"> 1</a></span>
<span class="normal"><a href="#__codelineno-0-2"> 2</a></span>
<span class="normal"><a href="#__codelineno-0-3"> 3</a></span>
<span class="normal"><a href="#__codelineno-0-4"> 4</a></span>
<span class="normal"><a href="#__codelineno-0-5"> 5</a></span>
<span class="normal"><a href="#__codelineno-0-6"> 6</a></span>
<span class="normal"><a href="#__codelineno-0-7"> 7</a></span>
<span class="normal"><a href="#__codelineno-0-8"> 8</a></span>
<span class="normal"><a href="#__codelineno-0-9"> 9</a></span>
<span class="normal"><a href="#__codelineno-0-10">10</a></span>
<span class="normal"><a href="#__codelineno-0-11">11</a></span>
<span class="normal"><a href="#__codelineno-0-12">12</a></span>
<span class="normal"><a href="#__codelineno-0-13">13</a></span>
<span class="normal"><a href="#__codelineno-0-14">14</a></span>
<span class="normal"><a href="#__codelineno-0-15">15</a></span>
<span class="normal"><a href="#__codelineno-0-16">16</a></span>
<span class="normal"><a href="#__codelineno-0-17">17</a></span>
<span class="normal"><a href="#__codelineno-0-18">18</a></span>
<span class="normal"><a href="#__codelineno-0-19">19</a></span>
<span class="normal"><a href="#__codelineno-0-20">20</a></span>
<span class="normal"><a href="#__codelineno-0-21">21</a></span>
<span class="normal"><a href="#__codelineno-0-22">22</a></span>
<span class="normal"><a href="#__codelineno-0-23">23</a></span>
<span class="normal"><a href="#__codelineno-0-24">24</a></span>
<span class="normal"><a href="#__codelineno-0-25">25</a></span>
<span class="normal"><a href="#__codelineno-0-26">26</a></span>
<span class="normal"><a href="#__codelineno-0-27">27</a></span>
<span class="normal"><a href="#__codelineno-0-28">28</a></span>
<span class="normal"><a href="#__codelineno-0-29">29</a></span>
<span class="normal"><a href="#__codelineno-0-30">30</a></span>
<span class="normal"><a href="#__codelineno-0-31">31</a></span>
<span class="normal"><a href="#__codelineno-0-32">32</a></span>
<span class="normal"><a href="#__codelineno-0-33">33</a></span>
<span class="normal"><a href="#__codelineno-0-34">34</a></span>
<span class="normal"><a href="#__codelineno-0-35">35</a></span>
<span class="normal"><a href="#__codelineno-0-36">36</a></span>
<span class="normal"><a href="#__codelineno-0-37">37</a></span>
<span class="normal"><a href="#__codelineno-0-38">38</a></span>
<span class="normal"><a href="#__codelineno-0-39">39</a></span>
<span class="normal"><a href="#__codelineno-0-40">40</a></span>
<span class="normal"><a href="#__codelineno-0-41">41</a></span>
<span class="normal"><a href="#__codelineno-0-42">42</a></span>
<span class="normal"><a href="#__codelineno-0-43">43</a></span>
<span class="normal"><a href="#__codelineno-0-44">44</a></span>
<span class="normal"><a href="#__codelineno-0-45">45</a></span>
<span class="normal"><a href="#__codelineno-0-46">46</a></span>
<span class="normal"><a href="#__codelineno-0-47">47</a></span>
<span class="normal"><a href="#__codelineno-0-48">48</a></span>
<span class="normal"><a href="#__codelineno-0-49">49</a></span>
<span class="normal"><a href="#__codelineno-0-50">50</a></span>
<span class="normal"><a href="#__codelineno-0-51">51</a></span>
<span class="normal"><a href="#__codelineno-0-52">52</a></span>
<span class="normal"><a href="#__codelineno-0-53">53</a></span>
<span class="normal"><a href="#__codelineno-0-54">54</a></span>
<span class="normal"><a href="#__codelineno-0-55">55</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1"></a><span class="c1">// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:</span>
<a id="__codelineno-0-2" name="__codelineno-0-2"></a><span class="c1">// https://github.com/microsoft/vscode-dev-containers/tree/v0.231.6/containers/dotnet-postgres</span>
<a id="__codelineno-0-3" name="__codelineno-0-3"></a><span class="p">{</span>
<a id="__codelineno-0-4" name="__codelineno-0-4"></a><span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Dotnet 6 API, Postgres &amp; SMTP&quot;</span><span class="p">,</span>
<a id="__codelineno-0-5" name="__codelineno-0-5"></a><span class="w"> </span><span class="nt">&quot;dockerComposeFile&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;docker-compose.yml&quot;</span><span class="p">,</span>
<a id="__codelineno-0-6" name="__codelineno-0-6"></a><span class="w"> </span><span class="nt">&quot;service&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;app&quot;</span><span class="p">,</span>
<a id="__codelineno-0-7" name="__codelineno-0-7"></a><span class="w"> </span><span class="nt">&quot;workspaceFolder&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;/workspace&quot;</span><span class="p">,</span>
<a id="__codelineno-0-8" name="__codelineno-0-8"></a>
<a id="__codelineno-0-9" name="__codelineno-0-9"></a><span class="w"> </span><span class="c1">// Set *default* container specific settings.json values on container create.</span>
<a id="__codelineno-0-10" name="__codelineno-0-10"></a><span class="w"> </span><span class="nt">&quot;settings&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-0-11" name="__codelineno-0-11"></a><span class="w"> </span><span class="nt">&quot;omnisharp.useModernNet&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="c1">//Use the build of OmniSharp that runs on the .NET 6 SDK. This build requires that the .NET 6 SDK be installed and does not use Visual Studio MSBuild tools or Mono. It only supports newer SDK-style projects that are buildable with dotnet build. Unity projects and other Full Framework projects are not supported. </span>
<a id="__codelineno-0-12" name="__codelineno-0-12"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-0-13" name="__codelineno-0-13"></a>
<a id="__codelineno-0-14" name="__codelineno-0-14"></a><span class="w"> </span><span class="c1">// Add the IDs of extensions you want installed when the container is created.</span>
<a id="__codelineno-0-15" name="__codelineno-0-15"></a><span class="w"> </span><span class="nt">&quot;extensions&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-0-16" name="__codelineno-0-16"></a><span class="w"> </span><span class="s2">&quot;formulahendry.auto-rename-tag&quot;</span><span class="p">,</span>
<a id="__codelineno-0-17" name="__codelineno-0-17"></a><span class="w"> </span><span class="s2">&quot;ms-dotnettools.csharp&quot;</span><span class="p">,</span>
<a id="__codelineno-0-18" name="__codelineno-0-18"></a><span class="w"> </span><span class="s2">&quot;EditorConfig.EditorConfig&quot;</span><span class="p">,</span>
<a id="__codelineno-0-19" name="__codelineno-0-19"></a><span class="w"> </span><span class="s2">&quot;dbaeumer.vscode-eslint&quot;</span><span class="p">,</span>
<a id="__codelineno-0-20" name="__codelineno-0-20"></a><span class="w"> </span><span class="s2">&quot;xabikos.JavaScriptSnippets&quot;</span><span class="p">,</span>
<a id="__codelineno-0-21" name="__codelineno-0-21"></a><span class="w"> </span><span class="s2">&quot;PKief.material-icon-theme&quot;</span><span class="p">,</span>
<a id="__codelineno-0-22" name="__codelineno-0-22"></a><span class="w"> </span><span class="s2">&quot;eg2.vscode-npm-script&quot;</span><span class="p">,</span>
<a id="__codelineno-0-23" name="__codelineno-0-23"></a><span class="w"> </span><span class="s2">&quot;christian-kohler.path-intellisense&quot;</span><span class="p">,</span>
<a id="__codelineno-0-24" name="__codelineno-0-24"></a><span class="w"> </span><span class="s2">&quot;esbenp.prettier-vscode&quot;</span><span class="p">,</span>
<a id="__codelineno-0-25" name="__codelineno-0-25"></a><span class="w"> </span><span class="s2">&quot;gencer.html-slim-scss-css-class-completion&quot;</span>
<a id="__codelineno-0-26" name="__codelineno-0-26"></a><span class="w"> </span><span class="p">],</span>
<a id="__codelineno-0-27" name="__codelineno-0-27"></a>
<a id="__codelineno-0-28" name="__codelineno-0-28"></a><span class="w"> </span><span class="c1">// Use &#39;forwardPorts&#39; to make a list of ports inside the container available locally.</span>
<a id="__codelineno-0-29" name="__codelineno-0-29"></a><span class="w"> </span><span class="c1">// &quot;forwardPorts&quot;: [5000, 5001],</span>
<a id="__codelineno-0-30" name="__codelineno-0-30"></a>
<a id="__codelineno-0-31" name="__codelineno-0-31"></a><span class="w"> </span><span class="c1">// [Optional] To reuse of your local HTTPS dev cert:</span>
<a id="__codelineno-0-32" name="__codelineno-0-32"></a><span class="w"> </span><span class="c1">//</span>
<a id="__codelineno-0-33" name="__codelineno-0-33"></a><span class="w"> </span><span class="c1">// 1. Export it locally using this command:</span>
<a id="__codelineno-0-34" name="__codelineno-0-34"></a><span class="w"> </span><span class="c1">// * Windows PowerShell:</span>
<a id="__codelineno-0-35" name="__codelineno-0-35"></a><span class="w"> </span><span class="c1">// dotnet dev-certs https --trust; dotnet dev-certs https -ep &quot;$env:USERPROFILE/.aspnet/https/aspnetapp.pfx&quot; -p &quot;SecurePwdGoesHere&quot;</span>
<a id="__codelineno-0-36" name="__codelineno-0-36"></a><span class="w"> </span><span class="c1">// * macOS/Linux terminal:</span>
<a id="__codelineno-0-37" name="__codelineno-0-37"></a><span class="w"> </span><span class="c1">// dotnet dev-certs https --trust; dotnet dev-certs https -ep &quot;${HOME}/.aspnet/https/aspnetapp.pfx&quot; -p &quot;SecurePwdGoesHere&quot;</span>
<a id="__codelineno-0-38" name="__codelineno-0-38"></a><span class="w"> </span><span class="c1">// </span>
<a id="__codelineno-0-39" name="__codelineno-0-39"></a><span class="w"> </span><span class="c1">// 2. Uncomment these &#39;remoteEnv&#39; lines:</span>
<a id="__codelineno-0-40" name="__codelineno-0-40"></a><span class="w"> </span><span class="c1">// &quot;remoteEnv&quot;: {</span>
<a id="__codelineno-0-41" name="__codelineno-0-41"></a><span class="w"> </span><span class="c1">// &quot;ASPNETCORE_Kestrel__Certificates__Default__Password&quot;: &quot;SecurePwdGoesHere&quot;,</span>
<a id="__codelineno-0-42" name="__codelineno-0-42"></a><span class="w"> </span><span class="c1">// &quot;ASPNETCORE_Kestrel__Certificates__Default__Path&quot;: &quot;/home/vscode/.aspnet/https/aspnetapp.pfx&quot;,</span>
<a id="__codelineno-0-43" name="__codelineno-0-43"></a><span class="w"> </span><span class="c1">// },</span>
<a id="__codelineno-0-44" name="__codelineno-0-44"></a><span class="w"> </span><span class="c1">//</span>
<a id="__codelineno-0-45" name="__codelineno-0-45"></a><span class="w"> </span><span class="c1">// 3. Next, copy your certificate into the container:</span>
<a id="__codelineno-0-46" name="__codelineno-0-46"></a><span class="w"> </span><span class="c1">// 1. Start the container</span>
<a id="__codelineno-0-47" name="__codelineno-0-47"></a><span class="w"> </span><span class="c1">// 2. Drag ~/.aspnet/https/aspnetapp.pfx into the root of the file explorer</span>
<a id="__codelineno-0-48" name="__codelineno-0-48"></a><span class="w"> </span><span class="c1">// 3. Open a terminal in VS Code and run &quot;mkdir -p /home/vscode/.aspnet/https &amp;&amp; mv aspnetapp.pfx /home/vscode/.aspnet/https&quot;</span>
<a id="__codelineno-0-49" name="__codelineno-0-49"></a>
<a id="__codelineno-0-50" name="__codelineno-0-50"></a><span class="w"> </span><span class="c1">// Use &#39;postCreateCommand&#39; to run commands after the container is created.</span>
<a id="__codelineno-0-51" name="__codelineno-0-51"></a><span class="w"> </span><span class="nt">&quot;postCreateCommand&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;dotnet restore&quot;</span><span class="p">,</span><span class="w"> </span><span class="c1">// had to use otherwise omnisharp wouldn&#39;t work properly, so no intellisense in vscode</span>
<a id="__codelineno-0-52" name="__codelineno-0-52"></a>
<a id="__codelineno-0-53" name="__codelineno-0-53"></a><span class="w"> </span><span class="c1">// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.</span>
<a id="__codelineno-0-54" name="__codelineno-0-54"></a><span class="w"> </span><span class="nt">&quot;remoteUser&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;vscode&quot;</span>
<a id="__codelineno-0-55" name="__codelineno-0-55"></a><span class="p">}</span>
</code></pre></div></td></tr></table></div>
<h3 id="docker-composeyml"><code>docker-compose.yml</code></h3>
<p>The docker compose file sets up the three components of this example project:</p>
<ol>
<li>The dotnet 6 API</li>
<li>The PostgreSQL database</li>
<li>The SMTP server</li>
</ol>
<p>Take note of the name of the SMTP server container as this will be needed later, as will the environment variables for the database. These should be changed from <code>postgres</code>.</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a>version: &#39;3.8&#39;
<a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a>
<a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a>services:
<a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a> app:
<a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a> build:
<a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a> context: .
<a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a> dockerfile: Dockerfile
<a id="__codelineno-1-8" name="__codelineno-1-8" href="#__codelineno-1-8"></a> args:
<a id="__codelineno-1-9" name="__codelineno-1-9" href="#__codelineno-1-9"></a> # Update &#39;VARIANT&#39; to pick a version of .NET: 3.1, 5.0, 6.0
<a id="__codelineno-1-10" name="__codelineno-1-10" href="#__codelineno-1-10"></a> VARIANT: &quot;6.0&quot;
<a id="__codelineno-1-11" name="__codelineno-1-11" href="#__codelineno-1-11"></a> # Optional version of Node.js
<a id="__codelineno-1-12" name="__codelineno-1-12" href="#__codelineno-1-12"></a> NODE_VERSION: &quot;lts/*&quot;
<a id="__codelineno-1-13" name="__codelineno-1-13" href="#__codelineno-1-13"></a>
<a id="__codelineno-1-14" name="__codelineno-1-14" href="#__codelineno-1-14"></a> volumes:
<a id="__codelineno-1-15" name="__codelineno-1-15" href="#__codelineno-1-15"></a> - ..:/workspace:cached
<a id="__codelineno-1-16" name="__codelineno-1-16" href="#__codelineno-1-16"></a>
<a id="__codelineno-1-17" name="__codelineno-1-17" href="#__codelineno-1-17"></a> # Overrides default command so things don&#39;t shut down after the process ends.
<a id="__codelineno-1-18" name="__codelineno-1-18" href="#__codelineno-1-18"></a> command: sleep infinity
<a id="__codelineno-1-19" name="__codelineno-1-19" href="#__codelineno-1-19"></a>
<a id="__codelineno-1-20" name="__codelineno-1-20" href="#__codelineno-1-20"></a> # Runs app on the same network as the database container, allows &quot;forwardPorts&quot; in devcontainer.json function.
<a id="__codelineno-1-21" name="__codelineno-1-21" href="#__codelineno-1-21"></a> network_mode: service:db
<a id="__codelineno-1-22" name="__codelineno-1-22" href="#__codelineno-1-22"></a>
<a id="__codelineno-1-23" name="__codelineno-1-23" href="#__codelineno-1-23"></a> # Uncomment the next line to use a non-root user for all processes.
<a id="__codelineno-1-24" name="__codelineno-1-24" href="#__codelineno-1-24"></a> # user: vscode
<a id="__codelineno-1-25" name="__codelineno-1-25" href="#__codelineno-1-25"></a>
<a id="__codelineno-1-26" name="__codelineno-1-26" href="#__codelineno-1-26"></a> # Use &quot;forwardPorts&quot; in **devcontainer.json** to forward an app port locally.
<a id="__codelineno-1-27" name="__codelineno-1-27" href="#__codelineno-1-27"></a> # (Adding the &quot;ports&quot; property to this file will not forward from a Codespace.)
<a id="__codelineno-1-28" name="__codelineno-1-28" href="#__codelineno-1-28"></a>
<a id="__codelineno-1-29" name="__codelineno-1-29" href="#__codelineno-1-29"></a> db:
<a id="__codelineno-1-30" name="__codelineno-1-30" href="#__codelineno-1-30"></a> image: postgres:14.1
<a id="__codelineno-1-31" name="__codelineno-1-31" href="#__codelineno-1-31"></a> restart: unless-stopped
<a id="__codelineno-1-32" name="__codelineno-1-32" href="#__codelineno-1-32"></a> volumes:
<a id="__codelineno-1-33" name="__codelineno-1-33" href="#__codelineno-1-33"></a> - postgres-data:/var/lib/postgresql/data
<a id="__codelineno-1-34" name="__codelineno-1-34" href="#__codelineno-1-34"></a> environment:
<a id="__codelineno-1-35" name="__codelineno-1-35" href="#__codelineno-1-35"></a> POSTGRES_PASSWORD: postgres
<a id="__codelineno-1-36" name="__codelineno-1-36" href="#__codelineno-1-36"></a> POSTGRES_USER: postgres
<a id="__codelineno-1-37" name="__codelineno-1-37" href="#__codelineno-1-37"></a> POSTGRES_DB: postgres
<a id="__codelineno-1-38" name="__codelineno-1-38" href="#__codelineno-1-38"></a>
<a id="__codelineno-1-39" name="__codelineno-1-39" href="#__codelineno-1-39"></a> # Add &quot;forwardPorts&quot;: [&quot;5432&quot;] to **devcontainer.json** to forward PostgreSQL locally.
<a id="__codelineno-1-40" name="__codelineno-1-40" href="#__codelineno-1-40"></a> # (Adding the &quot;ports&quot; property to this file will not forward from a Codespace.)
<a id="__codelineno-1-41" name="__codelineno-1-41" href="#__codelineno-1-41"></a>
<a id="__codelineno-1-42" name="__codelineno-1-42" href="#__codelineno-1-42"></a> mail:
<a id="__codelineno-1-43" name="__codelineno-1-43" href="#__codelineno-1-43"></a> image: bytemark/smtp
<a id="__codelineno-1-44" name="__codelineno-1-44" href="#__codelineno-1-44"></a> restart: unless-stopped
<a id="__codelineno-1-45" name="__codelineno-1-45" href="#__codelineno-1-45"></a>
<a id="__codelineno-1-46" name="__codelineno-1-46" href="#__codelineno-1-46"></a>volumes:
<a id="__codelineno-1-47" name="__codelineno-1-47" href="#__codelineno-1-47"></a> postgres-data:
</code></pre></div>
<h3 id="dockerfile"><code>Dockerfile</code></h3>
<p>This comes directly from Microsoft, I've made no modifications to it.</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="c"># [Choice] .NET version: 6.0, 5.0, 3.1, 6.0-bullseye, 5.0-bullseye, 3.1-bullseye, 6.0-focal, 5.0-focal, 3.1-focal</span>
<a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a><span class="k">ARG</span><span class="w"> </span><span class="nv">VARIANT</span><span class="o">=</span><span class="s2">&quot;6.0&quot;</span>
<a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a><span class="k">FROM</span><span class="w"> </span><span class="s">mcr.microsoft.com/vscode/devcontainers/dotnet:0-${VARIANT}</span>
<a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></a>
<a id="__codelineno-2-5" name="__codelineno-2-5" href="#__codelineno-2-5"></a><span class="c"># [Choice] Node.js version: none, lts/*, 16, 14, 12, 10</span>
<a id="__codelineno-2-6" name="__codelineno-2-6" href="#__codelineno-2-6"></a><span class="k">ARG</span><span class="w"> </span><span class="nv">NODE_VERSION</span><span class="o">=</span><span class="s2">&quot;none&quot;</span>
<a id="__codelineno-2-7" name="__codelineno-2-7" href="#__codelineno-2-7"></a><span class="k">RUN</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span><span class="s2">&quot;</span><span class="si">${</span><span class="nv">NODE_VERSION</span><span class="si">}</span><span class="s2">&quot;</span><span class="w"> </span>!<span class="o">=</span><span class="w"> </span><span class="s2">&quot;none&quot;</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span><span class="w"> </span>su<span class="w"> </span>vscode<span class="w"> </span>-c<span class="w"> </span><span class="s2">&quot;umask 0002 &amp;&amp; . /usr/local/share/nvm/nvm.sh &amp;&amp; nvm install </span><span class="si">${</span><span class="nv">NODE_VERSION</span><span class="si">}</span><span class="s2"> 2&gt;&amp;1&quot;</span><span class="p">;</span><span class="w"> </span><span class="k">fi</span>
<a id="__codelineno-2-8" name="__codelineno-2-8" href="#__codelineno-2-8"></a>
<a id="__codelineno-2-9" name="__codelineno-2-9" href="#__codelineno-2-9"></a><span class="c"># [Optional] Uncomment this section to install additional OS packages.</span>
<a id="__codelineno-2-10" name="__codelineno-2-10" href="#__codelineno-2-10"></a><span class="c"># RUN apt-get update &amp;&amp; export DEBIAN_FRONTEND=noninteractive \</span>
<a id="__codelineno-2-11" name="__codelineno-2-11" href="#__codelineno-2-11"></a><span class="c"># &amp;&amp; apt-get -y install --no-install-recommends &lt;your-package-list-here&gt;</span>
<a id="__codelineno-2-12" name="__codelineno-2-12" href="#__codelineno-2-12"></a>
<a id="__codelineno-2-13" name="__codelineno-2-13" href="#__codelineno-2-13"></a><span class="c"># [Optional] Uncomment this line to install global node packages.</span>
<a id="__codelineno-2-14" name="__codelineno-2-14" href="#__codelineno-2-14"></a><span class="c"># RUN su vscode -c &quot;source /usr/local/share/nvm/nvm.sh &amp;&amp; npm install -g &lt;your-package-here&gt;&quot; 2&gt;&amp;1</span>
</code></pre></div>
<h3 id="appsettingsjson-and-appsettingsdevelopmentjson"><code>appsettings.json</code> and <code>appsettings.Development.json</code></h3>
<p>To get the SMTP working, use these keys. If you're doing anything more fancy with <code>bytemark/smtp</code> than just the default functionality you may need to change the SMTP port.</p>
<div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-3-1">1</a></span>
<span class="normal"><a href="#__codelineno-3-2">2</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-3-1" name="__codelineno-3-1"></a><span class="nt">&quot;SmtpHost&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;mail&quot;</span><span class="p">,</span>
<a id="__codelineno-3-2" name="__codelineno-3-2"></a><span class="nt">&quot;SmtpPort&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">25</span><span class="p">,</span>
</code></pre></div></td></tr></table></div>
<p>To connect to the Postgres database set your connection string to <code>"User ID=postgres;Password=postgres;Host=db;Port=5432;Database=postgres;Pooling=true;"</code>, substituting in your chosen username, password and database name.</p>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
<div class="md-copyright__highlight">
Copyright &copy; 2011 - 2025 Josh Creek
</div>
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
<div class="md-social">
<a href="https://github.com/jcreek" target="_blank" rel="noopener" title="github.com" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8M97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</a>
<a href="https://twitter.com/jcreek23" target="_blank" rel="noopener" title="twitter.com" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253"/></svg>
</a>
<a href="https://www.linkedin.com/in/jrcreek" target="_blank" rel="noopener" title="www.linkedin.com" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3M135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5m282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9z"/></svg>
</a>
<a href="https://www.instagram.com/scruffy238/" target="_blank" rel="noopener" title="www.instagram.com" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M224.1 141c-63.6 0-114.9 51.3-114.9 114.9s51.3 114.9 114.9 114.9S339 319.5 339 255.9 287.7 141 224.1 141m0 189.6c-41.1 0-74.7-33.5-74.7-74.7s33.5-74.7 74.7-74.7 74.7 33.5 74.7 74.7-33.6 74.7-74.7 74.7m146.4-194.3c0 14.9-12 26.8-26.8 26.8-14.9 0-26.8-12-26.8-26.8s12-26.8 26.8-26.8 26.8 12 26.8 26.8m76.1 27.2c-1.7-35.9-9.9-67.7-36.2-93.9-26.2-26.2-58-34.4-93.9-36.2-37-2.1-147.9-2.1-184.9 0-35.8 1.7-67.6 9.9-93.9 36.1s-34.4 58-36.2 93.9c-2.1 37-2.1 147.9 0 184.9 1.7 35.9 9.9 67.7 36.2 93.9s58 34.4 93.9 36.2c37 2.1 147.9 2.1 184.9 0 35.9-1.7 67.7-9.9 93.9-36.2 26.2-26.2 34.4-58 36.2-93.9 2.1-37 2.1-147.8 0-184.8M398.8 388c-7.8 19.6-22.9 34.7-42.6 42.6-29.5 11.7-99.5 9-132.1 9s-102.7 2.6-132.1-9c-19.6-7.8-34.7-22.9-42.6-42.6-11.7-29.5-9-99.5-9-132.1s-2.6-102.7 9-132.1c7.8-19.6 22.9-34.7 42.6-42.6 29.5-11.7 99.5-9 132.1-9s102.7-2.6 132.1 9c19.6 7.8 34.7 22.9 42.6 42.6 11.7 29.5 9 99.5 9 132.1s2.7 102.7-9 132.1"/></svg>
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<div class="md-consent" data-md-component="consent" id="__consent" hidden>
<div class="md-consent__overlay"></div>
<aside class="md-consent__inner">
<form class="md-consent__form md-grid md-typeset" name="consent">
<h4>Cookie consent</h4>
<p>I use cookies to recognize your repeated visits and preferences, as well as to measure the effectiveness of my documentation and whether users find what they're searching for. With your consent, you're helping me to make my documentation better.</p>
<input class="md-toggle" type="checkbox" id="__settings" checked>
<div class="md-consent__settings">
<ul class="task-list">
<li class="task-list-item">
<label class="task-list-control">
<input type="checkbox" name="analytics" checked>
<span class="task-list-indicator"></span>
Google Analytics
</label>
</li>
<li class="task-list-item">
<label class="task-list-control">
<input type="checkbox" name="github" checked>
<span class="task-list-indicator"></span>
GitHub
</label>
</li>
</ul>
</div>
<div class="md-consent__controls">
<button class="md-button md-button--primary">Accept</button>
</div>
</form>
</aside>
</div>
<script>var consent=__md_get("__consent");if(consent)for(var input of document.forms.consent.elements)input.name&&(input.checked=consent[input.name]||!1);else"file:"!==location.protocol&&setTimeout((function(){document.querySelector("[data-md-component=consent]").hidden=!1}),250);var form=document.forms.consent;for(var action of["submit","reset"])form.addEventListener(action,(function(e){if(e.preventDefault(),"reset"===e.type)for(var n of document.forms.consent.elements)n.name&&(n.checked=!1);__md_set("__consent",Object.fromEntries(Array.from(new FormData(form).keys()).map((function(e){return[e,!0]})))),location.hash="",location.reload()}))</script>
<script id="__config" type="application/json">{"base": "../..", "features": ["navigation.instant", "navigation.tracking", "navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "toc.follow", "toc.integrate", "navigation.top", "search.suggest", "search.highlight", "search.share"], "search": "../../assets/javascripts/workers/search.f8cc74c7.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
<script src="../../assets/javascripts/bundle.c8b220af.min.js"></script>
</body>
</html>