dashy/docs/management/index.html

37 lines
137 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="generator" content="Docusaurus v2.0.0-beta.2">
<link rel="search" type="application/opensearchdescription+xml" title="Dashy" href="/opensearch.xml">
<script src="https://no-track.as93.net/js/script.js" defer="defer" data-domain="dashy.to"></script><title data-react-helmet="true">App Management | Dashy</title><meta data-react-helmet="true" property="og:url" content="https://dashy.to/docs/management"><meta data-react-helmet="true" name="docsearch:language" content="en"><meta data-react-helmet="true" name="docsearch:version" content="current"><meta data-react-helmet="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-react-helmet="true" property="og:title" content="App Management | Dashy"><meta data-react-helmet="true" name="description" content="The following article is a primer on managing self-hosted apps. It covers everything from keeping the Dashy (or any other app) up-to-date, secure, backed up, to other topics like auto-starting, monitoring, log management, web server configuration and using custom domains."><meta data-react-helmet="true" property="og:description" content="The following article is a primer on managing self-hosted apps. It covers everything from keeping the Dashy (or any other app) up-to-date, secure, backed up, to other topics like auto-starting, monitoring, log management, web server configuration and using custom domains."><link data-react-helmet="true" rel="shortcut icon" href="/img/favicon.ico"><link data-react-helmet="true" rel="canonical" href="https://dashy.to/docs/management"><link data-react-helmet="true" rel="alternate" href="https://dashy.to/docs/management" hreflang="en"><link data-react-helmet="true" rel="alternate" href="https://dashy.to/docs/management" hreflang="x-default"><link data-react-helmet="true" rel="preconnect" href="https://BH4D9OD16A-dsn.algolia.net" crossorigin="anonymous"><link rel="stylesheet" href="/assets/css/styles.de060916.css">
<link rel="preload" href="/assets/js/runtime~main.ac7349e8.js" as="script">
<link rel="preload" href="/assets/js/main.d13b237a.js" as="script">
</head>
<body>
<script>!function(){function t(t){document.documentElement.setAttribute("data-theme",t)}var e=function(){var t=null;try{t=localStorage.getItem("theme")}catch(t){}return t}();t(null!==e?e:"dark")}()</script><div id="__docusaurus">
<div><a href="#main" class="skipToContent_OuoZ">Skip to main content</a></div><nav class="navbar navbar--fixed-top"><div class="navbar__inner"><div class="navbar__items"><button aria-label="Navigation bar toggle" class="navbar__toggle clean-btn" type="button" tabindex="0"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/"><img src="/img/dashy.png" alt="Dashy Logo" class="themedImage_TMUO themedImage--light_4Vu1 navbar__logo"><img src="/img/dashy.png" alt="Dashy Logo" class="themedImage_TMUO themedImage--dark_uzRr navbar__logo"><b class="navbar__title">Dashy</b></a><a href="https://github.com/lissy93/dashy" target="_blank" rel="noopener noreferrer" class="navbar__item navbar__link">GitHub</a><a href="https://demo.dashy.to" target="_blank" rel="noopener noreferrer" class="navbar__item navbar__link">Live Demo</a><a class="navbar__item navbar__link" href="/docs/quick-start">Quick Start</a><a aria-current="page" class="navbar__item navbar__link navbar__link--active" href="/docs">Documentation</a></div><div class="navbar__items navbar__items--right"><div class="react-toggle displayOnlyInLargeViewport_cxYs react-toggle--checked react-toggle--disabled"><div class="react-toggle-track" role="button" tabindex="-1"><div class="react-toggle-track-check"><span class="toggle_iYfV">🌙</span></div><div class="react-toggle-track-x"><span class="toggle_iYfV">☀️</span></div><div class="react-toggle-thumb"></div></div><input type="checkbox" checked="" class="react-toggle-screenreader-only" aria-label="Switch between dark and light mode"></div><div class="searchBox_Bc3W"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><svg width="20" height="20" class="DocSearch-Search-Icon" viewBox="0 0 20 20" aria-hidden="true"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"></span></button></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div><div class="navbar-sidebar"><div class="navbar-sidebar__brand"><a class="navbar__brand" href="/"><img src="/img/dashy.png" alt="Dashy Logo" class="themedImage_TMUO themedImage--light_4Vu1 navbar__logo"><img src="/img/dashy.png" alt="Dashy Logo" class="themedImage_TMUO themedImage--dark_uzRr navbar__logo"><b class="navbar__title">Dashy</b></a></div><div class="navbar-sidebar__items"><div class="menu"><ul class="menu__list"><li class="menu__list-item"><a href="https://github.com/lissy93/dashy" target="_blank" rel="noopener noreferrer" class="menu__link">GitHub</a></li><li class="menu__list-item"><a href="https://demo.dashy.to" target="_blank" rel="noopener noreferrer" class="menu__link">Live Demo</a></li><li class="menu__list-item"><a class="menu__link" href="/docs/quick-start">Quick Start</a></li><li class="menu__list-item"><a aria-current="page" class="menu__link navbar__link--active" href="/docs">Documentation</a></li></ul></div></div></div></nav><div class="main-wrapper docs-wrapper doc-page"><div class="docPage_lDyR"><aside class="docSidebarContainer_0YBq"><div class="sidebar_LIo8"><nav class="menu menu--responsive thin-scrollbar menu_oAhv menuWithAnnouncementBar_IVfW" aria-label="Sidebar navigation"><button aria-label="Open menu" aria-haspopup="true" class="button button--secondary button--sm menu__button" type="button"><svg class="sidebarMenuIcon_nrF-" width="24" height="24" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><ul class="menu__list"><li class="menu__list-item"><a class="menu__link menu__link--sublist menu__link--active" href="#!">Running Dashy</a><ul class="menu__list"><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/docs/quick-start">Quick Start</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/docs/deployment">Deployment</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/docs/configuring">Configuring</a></li><li class="menu__list-item"><a aria-current="page" class="menu__link menu__link--active active" tabindex="0" href="/docs/management">App Management</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/docs/troubleshooting">Troubleshooting</a></li></ul></li><li class="menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#!">Feature Docs</a><ul class="menu__list"><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/icons">Icons</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/widgets">Widgets</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/theming">Theming</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/status-indicators">Status Indicators</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/authentication">Authentication</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/searching">Keyboard Shortcuts</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/alternate-views">Alternate Views &amp; Opening Methods</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/multi-language-support">Internationalization</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/backup-restore">Cloud Backup and Restore</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/pages-and-sections">Pages and Sections</a></li></ul></li><li class="menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#!">Community</a><ul class="menu__list"><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/showcase">*Dashy Showcase* 🌟</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/contributing">Contributing</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/developing">Developing</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/development-guides">Development Guides</a></li></ul></li><li class="menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#!">Misc</a><ul class="menu__list"><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/privacy">Privacy &amp; Security</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/changelog">Changelog</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/license">license</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/docs/code-of-conduct">Contributor Covenant Code of Conduct</a></li></ul></li></ul></nav><div class="sidebar-ad"><script async="" src="//cdn.carbonads.com/carbon.js?serve=CWYIC53L&amp;placement=dashyto" id="_carbonads_js"></script></div></div></aside><main class="docMainContainer_r8cw"><div class="container padding-top--md padding-bottom--lg"><div class="row"><div class="col docItemCol_zHA2"><div class="docItemContainer_oiyr"><article><div class="markdown"><header><h1 class="h1Heading_dC7a">App Management</h1></header><p><em>The following article is a primer on managing self-hosted apps. It covers everything from keeping the Dashy (or any other app) up-to-date, secure, backed up, to other topics like auto-starting, monitoring, log management, web server configuration and using custom domains.</em></p><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="contents"></a>Contents<a class="hash-link" href="#contents" title="Direct link to heading">#</a></h2><ul><li><a href="#providing-assets">Providing Assets</a></li><li><a href="#running-commands">Running Commands</a></li><li><a href="#healthchecks">Healthchecks</a></li><li><a href="#logs-and-performance">Logs and Performance</a></li><li><a href="#auto-starting-at-system-boot">Auto-Starting at Boot</a></li><li><a href="#updating">Updating</a></li><li><a href="#backing-up">Backing Up</a></li><li><a href="#scheduling">Scheduling</a></li><li><a href="#ssl-certificates">SSL Certificates</a></li><li><a href="#authentication">Authentication</a></li><li><a href="#managing-containers-with-docker-compose">Managing with Compose</a></li><li><a href="#passing-in-environmental-variables">Environmental Variables</a></li><li><a href="#setting-headers">Setting Headers</a></li><li><a href="#remote-access">Remote Access</a></li><li><a href="#custom-domain">Custom Domain</a></li><li><a href="#container-security">Securing Containers</a></li><li><a href="#web-server-configuration">Web Server Configuration</a></li><li><a href="#running-a-modified-version-of-the-app">Running a Modified App</a></li><li><a href="#building-your-own-container">Building your Own Container</a></li></ul><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="providing-assets"></a>Providing Assets<a class="hash-link" href="#providing-assets" title="Direct link to heading">#</a></h2><p>Although not essential, you will most likely want to provide several assets to your running app.</p><p>This is easy to do using <a href="https://docs.docker.com/storage/volumes/" target="_blank" rel="noopener noreferrer">Docker Volumes</a>, which lets you share a file or directory between your host system, and the container. Volumes are specified in the Docker run command, or Docker compose file, using the <code>--volume</code> or <code>-v</code> flags. The value of which consists of the path to the file / directory on your host system, followed by the destination path within the container. Fields are separated by a colon (<code>:</code>), and must be in the correct order. For example: <code>-v ~/alicia/my-local-conf.yml:/app/public/conf.yml</code></p><p>In Dashy, commonly configured resources include:</p><ul><li><code>./public/conf.yml</code> - Your main application config file</li><li><code>./public/item-icons</code> - A directory containing your own icons. This allows for offline access, and better performance than fetching from a CDN</li><li>Also within <code>./public</code> you&#x27;ll find standard website assets, including <code>favicon.ico</code>, <code>manifest.json</code>, <code>robots.txt</code>, etc. There&#x27;s no need to pass these in, but you can do so if you wish</li><li><code>/src/styles/user-defined-themes.scss</code> - A stylesheet for applying custom CSS to your app. You can also write your own themes here.</li></ul><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="running-commands"></a>Running Commands<a class="hash-link" href="#running-commands" title="Direct link to heading">#</a></h2><p>If you&#x27;re running an app in Docker, then commands will need to be passed to the container to be executed. This can be done by preceding each command with <code>docker exec -it [container-id]</code>, where container ID can be found by running <code>docker ps</code>. For example <code>docker exec -it 26c156c467b4 yarn build</code>. You can also enter the container, with <code>docker exec -it [container-id] /bin/ash</code>, and navigate around it with normal Linux commands.</p><p>Dashy has several commands that can be used for various tasks, you can find a list of these either in the <a href="/docs/developing#project-commands">Developing Docs</a>, or by looking at the <a href="https://github.com/Lissy93/dashy/blob/master/package.json#L5" target="_blank" rel="noopener noreferrer"><code>package.json</code></a>. These can be used by running <code>yarn [command-name]</code>.</p><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="healthchecks"></a>Healthchecks<a class="hash-link" href="#healthchecks" title="Direct link to heading">#</a></h2><p>Healthchecks are configured to periodically check that Dashy is up and running correctly on the specified port. By default, the health script is called every 5 minutes, but this can be modified with the <code>--health-interval</code> option. You can check the current container health with: <code>docker inspect --format &quot;{{json .State.Health }}&quot; [container-id]</code>, and a summary of health status will show up under <code>docker ps</code>. You can also manually request the current application status by running <code>docker exec -it [container-id] yarn health-check</code>. You can disable healthchecks altogether by adding the <code>--no-healthcheck</code> flag to your Docker run command.</p><p>To restart unhealthy containers automatically, check out <a href="https://hub.docker.com/r/willfarrell/autoheal/" target="_blank" rel="noopener noreferrer">Autoheal</a>. This image watches for unhealthy containers, and automatically triggers a restart. (This is a stand in for Docker&#x27;s <code>--exit-on-unhealthy</code> that was proposed, but <a href="https://github.com/moby/moby/pull/22719" target="_blank" rel="noopener noreferrer">not merged</a>). There&#x27;s also <a href="https://github.com/qdm12/deunhealth" target="_blank" rel="noopener noreferrer">Deunhealth</a>, which is super light-weight, and doesn&#x27;t require network access.</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI bash"><pre tabindex="0" class="prism-code language-bash codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">docker</span><span class="token plain"> run -d </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> --name autoheal </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> --restart</span><span class="token operator">=</span><span class="token plain">always </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> -e </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">AUTOHEAL_CONTAINER_LABEL</span><span class="token operator">=</span><span class="token plain">all </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> -v /var/run/docker.sock:/var/run/docker.sock </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> willfarrell/autoheal</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="logs-and-performance"></a>Logs and Performance<a class="hash-link" href="#logs-and-performance" title="Direct link to heading">#</a></h2><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="container-logs"></a>Container Logs<a class="hash-link" href="#container-logs" title="Direct link to heading">#</a></h3><p>You can view logs for a given Docker container with <code>docker logs [container-id]</code>, add the <code>--follow</code> flag to stream the logs. For more info, see the <a href="https://docs.docker.com/config/containers/logging/" target="_blank" rel="noopener noreferrer">Logging Documentation</a>. There&#x27;s also <a href="https://dozzle.dev/" target="_blank" rel="noopener noreferrer">Dozzle</a>, a useful tool, that provides a web interface where you can stream and query logs from all your running containers from a single web app.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="container-performance"></a>Container Performance<a class="hash-link" href="#container-performance" title="Direct link to heading">#</a></h3><p>You can check the resource usage for your running Docker containers with <code>docker stats</code> or <code>docker stats [container-id]</code>. For more info, see the <a href="https://docs.docker.com/engine/reference/commandline/stats/" target="_blank" rel="noopener noreferrer">Stats Documentation</a>. There&#x27;s also <a href="https://github.com/google/cadvisor" target="_blank" rel="noopener noreferrer">cAdvisor</a>, a useful web app for viewing and analyzing resource usage and performance of all your running containers.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="management-apps"></a>Management Apps<a class="hash-link" href="#management-apps" title="Direct link to heading">#</a></h3><p>You can also view logs, resource usage and other info as well as manage your entire Docker workflow in third-party Docker management apps. For example <a href="https://github.com/portainer/portainer" target="_blank" rel="noopener noreferrer">Portainer</a> an all-in-one open source management web UI for Docker and Kubernetes, or <a href="https://github.com/jesseduffield/lazydocker" target="_blank" rel="noopener noreferrer">LazyDocker</a> a terminal UI for Docker container management and monitoring.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="advanced-logging-and-monitoring"></a>Advanced Logging and Monitoring<a class="hash-link" href="#advanced-logging-and-monitoring" title="Direct link to heading">#</a></h3><p>Docker supports using <a href="https://prometheus.io/" target="_blank" rel="noopener noreferrer">Prometheus</a> to collect logs, which can then be visualized using a platform like <a href="https://grafana.com/" target="_blank" rel="noopener noreferrer">Grafana</a>. For more info, see <a href="https://docs.docker.com/config/daemon/prometheus/" target="_blank" rel="noopener noreferrer">this guide</a>. If you need to route your logs to a remote syslog, then consider using <a href="https://github.com/gliderlabs/logspout" target="_blank" rel="noopener noreferrer">logspout</a>. For enterprise-grade instances, there are managed services, that make monitoring container logs and metrics very easy, such as <a href="https://sematext.com/blog/docker-container-monitoring-with-sematext/" target="_blank" rel="noopener noreferrer">Sematext</a> with <a href="https://github.com/sematext/logagent-js" target="_blank" rel="noopener noreferrer">Logagent</a>.</p><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="auto-starting-at-system-boot"></a>Auto-Starting at System Boot<a class="hash-link" href="#auto-starting-at-system-boot" title="Direct link to heading">#</a></h2><p>You can use Docker&#x27;s <a href="https://docs.docker.com/engine/reference/run/#restart-policies---restart" target="_blank" rel="noopener noreferrer">restart policies</a> to instruct the container to start after a system reboot, or restart after a crash. Just add the <code>--restart=always</code> flag to your Docker compose script or Docker run command. For more information, see the docs on <a href="https://docs.docker.com/config/containers/start-containers-automatically/" target="_blank" rel="noopener noreferrer">Starting Containers Automatically</a>.</p><p>For Podman, you can use <code>systemd</code> to create a service that launches your container, <a href="https://podman.io/blogs/2018/09/13/systemd.html" target="_blank" rel="noopener noreferrer">the docs</a> explains things further. A similar approach can be used with Docker, if you need to start containers after a reboot, but before any user interaction.</p><p>To restart the container after something within it has crashed, consider using <a href="https://github.com/willfarrell/docker-autoheal" target="_blank" rel="noopener noreferrer"><code>docker-autoheal</code></a> by @willfarrell, a service that monitors and restarts unhealthy containers. For more info, see the <a href="#healthchecks">Healthchecks</a> section above.</p><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="updating"></a>Updating<a class="hash-link" href="#updating" title="Direct link to heading">#</a></h2><p>Dashy is under active development, so to take advantage of the latest features, you may need to update your instance every now and again.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="updating-docker-container"></a>Updating Docker Container<a class="hash-link" href="#updating-docker-container" title="Direct link to heading">#</a></h3><ol><li>Pull latest image: <code>docker pull lissy93/dashy:latest</code></li><li>Kill off existing container<ul><li>Find container ID: <code>docker ps</code></li><li>Stop container: <code>docker stop [container_id]</code></li><li>Remove container: <code>docker rm [container_id]</code></li></ul></li><li>Spin up new container: <code>docker run [params] lissy93/dashy</code></li></ol><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="automatic-docker-updates"></a>Automatic Docker Updates<a class="hash-link" href="#automatic-docker-updates" title="Direct link to heading">#</a></h3><p>You can automate the above process using <a href="https://github.com/containrrr/watchtower" target="_blank" rel="noopener noreferrer">Watchtower</a>.
Watchtower will watch for new versions of a given image on Docker Hub, pull down your new image, gracefully shut down your existing container and restart it with the same options that were used when it was deployed initially.</p><p>To get started, spin up the watchtower container:</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI bash"><pre tabindex="0" class="prism-code language-bash codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">docker</span><span class="token plain"> run -d </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> --name watchtower </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> -v /var/run/docker.sock:/var/run/docker.sock </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> containrrr/watchtower</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p>For more information, see the <a href="https://containrrr.dev/watchtower/" target="_blank" rel="noopener noreferrer">Watchtower Docs</a></p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="updating-dashy-from-source"></a>Updating Dashy from Source<a class="hash-link" href="#updating-dashy-from-source" title="Direct link to heading">#</a></h3><p>Stop your current instance of Dashy, then navigate into the source directory. Pull down the latest code, with <code>git pull origin master</code>, then update dependencies with <code>yarn</code>, rebuild with <code>yarn build</code>, and start the server again with <code>yarn start</code>.</p><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="backing-up"></a>Backing Up<a class="hash-link" href="#backing-up" title="Direct link to heading">#</a></h2><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="backing-up-containers"></a>Backing Up Containers<a class="hash-link" href="#backing-up-containers" title="Direct link to heading">#</a></h3><p>You can make a backup of any running container really easily, using <a href="https://docs.docker.com/engine/reference/commandline/commit/" target="_blank" rel="noopener noreferrer"><code>docker commit</code></a> and save it with <a href="https://docs.docker.com/engine/reference/commandline/export/" target="_blank" rel="noopener noreferrer"><code>docker export</code></a>, to do so:</p><ul><li>First find the container ID, you can do this with <code>docker container ls</code></li><li>Now to create the snapshot, just run <code>docker commit -p [container-id] my-backup</code></li><li>Finally, to save the backup locally, run <code>docker save -o ~/dashy-backup.tar my-backup</code></li><li>If you want to push this to a container registry, run <code>docker push my-backup:latest</code></li></ul><p>Note that this will not include any data in docker volumes, and the process here is a bit different. Since these files exist on your host system, if you have an existing backup solution implemented, you can incorporate and volume files within that system.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="backing-up-volumes"></a>Backing Up Volumes<a class="hash-link" href="#backing-up-volumes" title="Direct link to heading">#</a></h3><p><a href="https://github.com/offen/docker-volume-backup" target="_blank" rel="noopener noreferrer">offen/docker-volume-backup</a> is a useful tool for periodic Docker volume backups, to any S3-compatible storage provider. It&#x27;s run as a light-weight Docker container, and is easy to setup, and also supports GPG-encryption, email notification, and routing away older backups.</p><p>To get started, create a docker-compose similar to the example below, and then start the container. For more info, check out their <a href="https://github.com/offen/docker-volume-backup" target="_blank" rel="noopener noreferrer">documentation</a>, which is very clear.</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI yaml"><pre tabindex="0" class="prism-code language-yaml codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token key atrule">version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">&#x27;3&#x27;</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">services</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">backup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> offen/docker</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">volume</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">backup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">latest</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">environment</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">BACKUP_CRON_EXPRESSION</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">&quot;0 * * * *&quot;</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">BACKUP_PRUNING_PREFIX</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> backup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">BACKUP_RETENTION_DAYS</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">7</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">AWS_BUCKET_NAME</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> backup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">bucket</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">AWS_ACCESS_KEY_ID</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> AKIAIOSFODNN7EXAMPLE</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">AWS_SECRET_ACCESS_KEY</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">volumes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">/backup/my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">app</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">backup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">ro</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> /var/run/docker.sock</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">/var/run/docker.sock</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">ro</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">volumes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p>It&#x27;s worth noting that this process can also be done manually, using the following commands:</p><p>Backup:</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI bash"><pre tabindex="0" class="prism-code language-bash codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">docker</span><span class="token plain"> run --rm -v some_volume:/volume -v /tmp:/backup alpine </span><span class="token function" style="color:rgb(80, 250, 123)">tar</span><span class="token plain"> -cjf /backup/some_archive.tar.bz2 -C /volume ./</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p>Restore:</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI bash"><pre tabindex="0" class="prism-code language-bash codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">docker</span><span class="token plain"> run --rm -v some_volume:/volume -v /tmp:/backup alpine </span><span class="token function" style="color:rgb(80, 250, 123)">sh</span><span class="token plain"> -c </span><span class="token string" style="color:rgb(255, 121, 198)">&quot;rm -rf /volume/* /volume/..?* /volume/.[!.]* ; tar -C /volume/ -xjf /backup/some_archive.tar.bz2&quot;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="dashy-specific-backup"></a>Dashy-Specific Backup<a class="hash-link" href="#dashy-specific-backup" title="Direct link to heading">#</a></h3><p>Since Dashy is open source, and freely available, providing you&#x27;re configuration data is passed in as volumes, there shouldn&#x27;t be any need to backup the main container. Your main config file, and any assets you&#x27;re using should be kept backed up, preferably in at least two places, and you should ensure that you can easily restore from backup, if needed.</p><p>Dashy also has a built-in cloud backup feature, which is free for personal users, and will let you make and restore fully encrypted backups of your config directly through the UI. To learn more, see the <a href="/docs/backup-restore">Cloud Backup Docs</a></p><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="scheduling"></a>Scheduling<a class="hash-link" href="#scheduling" title="Direct link to heading">#</a></h2><p>If you need to periodically schedule the running of a given command on Dashy (or any other container), then a useful tool for doing so it <a href="https://github.com/mcuadros/ofelia" target="_blank" rel="noopener noreferrer">ofelia</a>. This runs as a Docker container and is really useful for things like backups, logging, updating, notifications, etc. Crons are specified using Go&#x27;s crontab format, and a useful tool for visualizing this is <a href="https://crontab.guru/" target="_blank" rel="noopener noreferrer">crontab.guru</a>. This can also be done natively with Alpine: <code>docker run -it alpine ls /etc/periodic</code>.
I recommend combining this with <a href="https://github.com/healthchecks/healthchecks" target="_blank" rel="noopener noreferrer">healthchecks</a> for easy monitoring of jobs, and failure notifications.</p><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="ssl-certificates"></a>SSL Certificates<a class="hash-link" href="#ssl-certificates" title="Direct link to heading">#</a></h2><p>Enabling HTTPS with an SSL certificate is recommended, especially if you are hosting Dashy anywhere other than your home. This will ensure that all traffic is encrypted in transit.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="auto-ssl"></a>Auto-SSL<a class="hash-link" href="#auto-ssl" title="Direct link to heading">#</a></h3><p>If you are using <a href="https://nginxproxymanager.com/" target="_blank" rel="noopener noreferrer">NGINX Proxy Manager</a>, then SSL is supported out of the box. Once you&#x27;ve added your proxy host and web address, then set the scheme to HTTPS, then under the SSL Tab select &quot;Request a new SSL certificate&quot; and follow the on-screen instructions.</p><p>If you&#x27;re hosting Dashy behind Cloudflare, then they offer <a href="https://www.cloudflare.com/en-gb/learning/ssl/what-is-an-ssl-certificate/" target="_blank" rel="noopener noreferrer">free and easy SSL</a>- all you need to do is enable it under the SSL/TLS tab. Or if you are using shared hosting, you may find <a href="https://www.sitepoint.com/a-guide-to-setting-up-lets-encrypt-ssl-on-shared-hosting/" target="_blank" rel="noopener noreferrer">this tutorial</a> helpful.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="getting-a-self-signed-ssl-certificate"></a>Getting a Self-Signed SSL Certificate<a class="hash-link" href="#getting-a-self-signed-ssl-certificate" title="Direct link to heading">#</a></h3><p><a href="https://letsencrypt.org/docs/" target="_blank" rel="noopener noreferrer">Let&#x27;s Encrypt</a> is a global Certificate Authority, providing free SSL/TLS Domain Validation certificates in order to enable secure HTTPS access to your website. They have good browser/ OS <a href="https://letsencrypt.org/docs/certificate-compatibility/" target="_blank" rel="noopener noreferrer">compatibility</a> with their ISRG X1 and DST CA X3 root certificates, support <a href="https://community.letsencrypt.org/t/acme-v2-production-environment-wildcards/55578" target="_blank" rel="noopener noreferrer">Wildcard issuance</a> done via ACMEv2 using the DNS-01 and have <a href="https://letsencrypt.org/2020/02/19/multi-perspective-validation.html" target="_blank" rel="noopener noreferrer">Multi-Perspective Validation</a>. Let&#x27;s Encrypt provide <a href="https://certbot.eff.org/" target="_blank" rel="noopener noreferrer">CertBot</a> an easy app for generating and setting up an SSL certificate.</p><p>This process can be automated, using something like the <a href="https://github.com/Valian/docker-nginx-auto-ssl" target="_blank" rel="noopener noreferrer">Docker-NGINX-Auto-SSL Container</a> to generate and renew certificates when needed.</p><p>If you&#x27;re not so comfortable on the command line, then you can use a tool like <a href="https://www.sslforfree.com/" target="_blank" rel="noopener noreferrer">SSL For Free</a> or <a href="https://zerossl.com/" target="_blank" rel="noopener noreferrer">ZeroSSL</a> to generate your cert. They also provide step-by-step setup instructions for most platforms.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="passing-a-self-signed-certificate-to-dashy"></a>Passing a Self-Signed Certificate to Dashy<a class="hash-link" href="#passing-a-self-signed-certificate-to-dashy" title="Direct link to heading">#</a></h3><p>Once you&#x27;ve generated your SSL cert, you&#x27;ll need to pass it to Dashy. This can be done by specifying the paths to your public and private keys using the <code>SSL_PRIV_KEY_PATH</code> and <code>SSL_PUB_KEY_PATH</code> environmental variables. Or if you&#x27;re using Docker, then just pass public + private SSL keys in under <code>/etc/ssl/certs/dashy-pub.pem</code> and <code>/etc/ssl/certs/dashy-priv.key</code> respectively, e.g:</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI bash"><pre tabindex="0" class="prism-code language-bash codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">docker</span><span class="token plain"> run -d </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> -p </span><span class="token number">8080</span><span class="token plain">:80 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> -v ~/my-private-key.key:/etc/ssl/certs/dashy-priv.key:ro </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> -v ~/my-public-key.pem:/etc/ssl/certs/dashy-pub.pem:ro </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> lissy93/dashy:latest</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p>By default the SSL port is <code>443</code> within a Docker container, or <code>4001</code> if running on bare metal, but you can override this with the <code>SSL_PORT</code> environmental variable.</p><p>Once everything is setup, you can verify your site is secured using a tool like <a href="https://www.sslchecker.com/sslchecker" target="_blank" rel="noopener noreferrer">SSL Checker</a>.</p><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="authentication"></a>Authentication<a class="hash-link" href="#authentication" title="Direct link to heading">#</a></h2><p>Dashy natively supports secure authentication using KeyCloak. There is also a Simple Auth feature that doesn&#x27;t require any additional setup. Usage instructions for both, as well as alternative auth methods, has now moved to the <strong><a href="/docs/authentication">Authentication Docs</a></strong> page.</p><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="managing-containers-with-docker-compose"></a>Managing Containers with Docker Compose<a class="hash-link" href="#managing-containers-with-docker-compose" title="Direct link to heading">#</a></h2><p>When you have a lot of containers, it quickly becomes hard to manage with <code>docker run</code> commands. The solution to this is <a href="https://docs.docker.com/compose/" target="_blank" rel="noopener noreferrer">docker compose</a>, a handy tool for defining all a containers run settings in a single YAML file, and then spinning up that container with a single short command - <code>docker compose up</code>. A good example of which can be seen in <a href="https://github.com/abhilesh/self-hosted_docker_setups" target="_blank" rel="noopener noreferrer">@abhilesh&#x27;s docker compose collection</a>.</p><p>You can use Dashy&#x27;s default <a href="https://github.com/Lissy93/dashy/blob/master/docker-compose.yml" target="_blank" rel="noopener noreferrer"><code>docker-compose.yml</code></a> file as a template, and modify it according to your needs.</p><p>An example Docker compose, using the default base image from DockerHub, might look something like this:</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI yaml"><pre tabindex="0" class="prism-code language-yaml codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">---</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">&quot;3.8&quot;</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">services</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">dashy</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">container_name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Dashy</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> lissy93/dashy</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">volumes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> /root/my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">config.yml</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">/app/public/conf.yml</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">ports</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> 4000</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token number">80</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">environment</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> BASE_URL=/my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">dashboard</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">restart</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> unless</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">stopped</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">healthcheck</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">test</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">&#x27;CMD&#x27;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">&#x27;node&#x27;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">&#x27;/app/services/healthcheck&#x27;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">interval</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> 1m30s</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">timeout</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> 10s</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">retries</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">3</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">start_period</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> 40s</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="passing-in-environmental-variables"></a>Passing in Environmental Variables<a class="hash-link" href="#passing-in-environmental-variables" title="Direct link to heading">#</a></h2><p>With Docker, you can define environmental variables under the <code>environment</code> section of your Docker compose file. Environmental variables are used to configure high-level settings, usually before the config file has been read. For a list of all supported env vars in Dashy, see <a href="/docs/developing#environmental-variables">the developing docs</a>, or the default <a href="https://github.com/Lissy93/dashy/blob/master/.env" target="_blank" rel="noopener noreferrer"><code>.env</code></a> file.</p><p>A common use case, is to run Dashy under a sub-page, instead of at the root of a URL (e.g. <code>https://my-homelab.local/dashy</code> instead of <code>https://dashy.my-homelab.local</code>). In this use-case, you&#x27;d specify the <code>BASE_URL</code> variable in your compose file.</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI yaml"><pre tabindex="0" class="prism-code language-yaml codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token key atrule">environment</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> BASE_URL=/dashy</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p>You can also do the same thing with the docker run command, using the <a href="https://docs.docker.com/engine/reference/commandline/run/#set-environment-variables--e---env---env-file" target="_blank" rel="noopener noreferrer"><code>--env</code></a> flag.
If you&#x27;ve got many environmental variables, you might find it useful to put them in a <a href="https://docs.docker.com/compose/env-file/" target="_blank" rel="noopener noreferrer"><code>.env</code> file</a>. Similarly, for Docker run you can use <a href="https://docs.docker.com/engine/reference/commandline/run/#set-environment-variables--e---env---env-file" target="_blank" rel="noopener noreferrer"><code>--env-file</code></a> if you&#x27;d like to pass in a file containing all your environmental variables.</p><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="setting-headers"></a>Setting Headers<a class="hash-link" href="#setting-headers" title="Direct link to heading">#</a></h2><p>Any external requests made to a different origin (app/ service under a different domain) will be blocked if the correct headers are not specified. This is known as <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" target="_blank" rel="noopener noreferrer">Cross-Origin Resource Sharing</a> (CORS) and is a security feature built into modern browsers.</p><p>If you see a CORS error in your console, this can be easily fixed by setting the correct headers. This is not a bug with Dashy, so please don&#x27;t raise it as a bug!</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="example-headers"></a>Example Headers<a class="hash-link" href="#example-headers" title="Direct link to heading">#</a></h3><ul><li><a href="#caddy">Caddy</a></li><li><a href="#nginx">NGINX</a></li><li><a href="#traefik">Træfɪk</a></li><li><a href="#haproxy">HAProxy</a></li><li><a href="#apache">Apache</a></li></ul><p><em>The following section briefly outlines how you can set headers for common web proxies/ servers. More info can be found in the documentation for the proxy that you are using, or in the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" target="_blank" rel="noopener noreferrer">MDN Docs</a>.</em></p><p>These examples are using:</p><ul><li><code>Access-Control-Allow-Origin</code> header, but depending on what type of content you are enabling, this will vary. For example, to allow a site to be loaded in an iframe (for the modal or workspace views) you would use <code>X-Frame-Options</code>.</li><li>The domain root (<code>/</code>), if your&#x27;re hosting from a sub-page, replace that with your path.</li><li>A wildcard (<code>*</code>), which would allow access from traffic on any domain, this is discouraged, and you should replace it with the URL where you are hosting Dashy. Note that for requests that transport sensitive info, like credentials (e.g. Keycloak login), the wildcard is <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#requests_with_credentials" target="_blank" rel="noopener noreferrer">disallowed all together</a> and will be blocked.</li></ul><h4><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="caddy"></a>Caddy<a class="hash-link" href="#caddy" title="Direct link to heading">#</a></h4><blockquote><p>See <a href="https://caddyserver.com/docs/caddyfile/directives/header" target="_blank" rel="noopener noreferrer">Caddy <code>header</code> docs</a> for more info.</p></blockquote><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI text"><pre tabindex="0" class="prism-code language-text codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain">headers / {</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Access-Control-Allow-Origin *</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><h4><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="nginx"></a>NGINX<a class="hash-link" href="#nginx" title="Direct link to heading">#</a></h4><blockquote><p>See <a href="https://nginx.org/en/docs/http/ngx_http_headers_module.html" target="_blank" rel="noopener noreferrer">NGINX <code>ngx_http_headers_module</code> docs</a> for more info.</p></blockquote><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI text"><pre tabindex="0" class="prism-code language-text codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain">location / {</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> add_header Access-Control-Allow-Origin *;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p>Note this can also be done through the UI, using NGINX Proxy Manager.</p><h4><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="traefik"></a>Traefik<a class="hash-link" href="#traefik" title="Direct link to heading">#</a></h4><blockquote><p>See <a href="https://doc.traefik.io/traefik/middlewares/http/headers/#cors-headers" target="_blank" rel="noopener noreferrer">Træfɪk CORS headers docs</a> for more info.</p></blockquote><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI text"><pre tabindex="0" class="prism-code language-text codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain">labels:</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> - &quot;traefik.http.middlewares.testheader.headers.accesscontrolallowmethods=GET,OPTIONS,PUT&quot;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> - &quot;traefik.http.middlewares.testheader.headers.accesscontrolalloworiginlist=https://foo.bar.org,https://example.org&quot;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> - &quot;traefik.http.middlewares.testheader.headers.accesscontrolmaxage=100&quot;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> - &quot;traefik.http.middlewares.testheader.headers.addvaryheader=true&quot;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><h4><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="haproxy"></a>HAProxy<a class="hash-link" href="#haproxy" title="Direct link to heading">#</a></h4><blockquote><p>See <a href="https://www.haproxy.com/documentation/hapee/latest/traffic-routing/rewrites/rewrite-responses/" target="_blank" rel="noopener noreferrer">HAProxy Rewrite Response Docs</a> for more info.</p></blockquote><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI text"><pre tabindex="0" class="prism-code language-text codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain">/</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> http-response add-header Access-Control-Allow-Origin *</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><h4><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="apache"></a>Apache<a class="hash-link" href="#apache" title="Direct link to heading">#</a></h4><blockquote><p>See <a href="https://httpd.apache.org/docs/current/mod/mod_headers.html" target="_blank" rel="noopener noreferrer">Apache <code>mode_headers</code> docs</a> for more info.</p></blockquote><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI text"><pre tabindex="0" class="prism-code language-text codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain">Header always set Access-Control-Allow-Origin &quot;*&quot;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><h4><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="squid"></a>Squid<a class="hash-link" href="#squid" title="Direct link to heading">#</a></h4><blockquote><p>See <a href="http://www2.gr.squid-cache.org/Doc/config/request_header_access/" target="_blank" rel="noopener noreferrer">Squid <code>request_header_access</code> docs</a> for more info.</p></blockquote><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI text"><pre tabindex="0" class="prism-code language-text codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain">request_header_access Authorization allow all</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="remote-access"></a>Remote Access<a class="hash-link" href="#remote-access" title="Direct link to heading">#</a></h2><ul><li><a href="#wireguard">WireGuard</a></li><li><a href="#reverse-ssh-tunnel">Reverse SSH Tunnel</a></li><li><a href="#tcp-tunnel">TCP Tunnel</a></li></ul><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="wireguard"></a>WireGuard<a class="hash-link" href="#wireguard" title="Direct link to heading">#</a></h3><p>Using a VPN is one of the easiest ways to provide secure, full access to your local network from remote locations. <a href="https://www.wireguard.com/" target="_blank" rel="noopener noreferrer">WireGuard</a> is a reasonably new open source VPN protocol, that was designed with ease of use, performance and security in mind. Unlike OpenVPN, it doesn&#x27;t need to recreate the tunnel whenever connection is dropped, and it&#x27;s also much easier to setup, using shared keys instead.</p><ul><li><strong>Install Wireguard</strong> - See the <a href="https://www.wireguard.com/install/" target="_blank" rel="noopener noreferrer">Install Docs</a> for download links + instructions<ul><li>On Debian-based systems, it&#x27;s <code>sudo apt install wireguard</code></li></ul></li><li><strong>Generate a Private Key</strong> - Run <code>wg genkey</code> on the Wireguard server, and copy it to somewhere safe for later</li><li><strong>Create Server Config</strong> - Open or create a file at <code>/etc/wireguard/wg0.conf</code> and under <code>[Interface]</code> add the following (see example below):<ul><li><code>Address</code> - as a subnet of all desired IPs</li><li><code>PrivateKey</code> - that you just generated</li><li><code>ListenPort</code> - Default is <code>51820</code>, but can be anything</li></ul></li><li><strong>Get Client App</strong> - Download the <a href="https://www.wireguard.com/install/" target="_blank" rel="noopener noreferrer">WG client app</a> for your platform (Linux, Windows, MacOS, Android or iOS are all supported)</li><li><strong>Create new Client Tunnel</strong> - On your client app, there should be an option to create a new tunnel, when doing so a client private key will be generated (but if not, use the <code>wg genkey</code> command again), and keep it somewhere safe. A public key will also be generated, and this will go in our saver config</li><li><strong>Add Clients to Server Config</strong> - Head back to your <code>wg0.conf</code> file on the server, create a <code>[Peer]</code> section, and populate the following info<ul><li><code>AllowedIPs</code> - List of IP address inside the subnet, the client should have access to</li><li><code>PublicKey</code> - The public key for the client you just generated</li></ul></li><li><strong>Start the Server</strong> - You can now start the WG server, using: <code>wg-quick up wg0</code> on your server</li><li><strong>Finish Client Setup</strong> - Head back to your client device, and edit the config file, leave the private key as is, and add the following fields:<ul><li><code>PublicKey</code> - The public key of the server</li><li><code>Address</code> - This should match the <code>AllowedIPs</code> section on the servers config file</li><li><code>DNS</code> - The DNS server that&#x27;ll be used when accessing the network through the VPN</li><li><code>Endpoint</code> - The hostname or IP + Port where your WG server is running (you may need to forward this in your firewall&#x27;s settings)</li></ul></li><li><strong>Done</strong> - Your clients should now be able to connect to your WG server :) Depending on your networks firewall rules, you may need to port forward the address of your WG server</li></ul><h4><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="example-server-config"></a><strong>Example Server Config</strong><a class="hash-link" href="#example-server-config" title="Direct link to heading">#</a></h4><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI ini"><pre tabindex="0" class="prism-code language-ini codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Server file</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">[Interface]</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Which networks does my interface belong to? Notice: /24 and /64</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Address = 10.5.0.1/24, 2001:470:xxxx:xxxx::1/64</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">PrivateKey = xxx</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">ListenPort = 51820</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Peer 1</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">[Peer]</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">PublicKey = xxx</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Which source IPs can I expect from that peer? Notice: /32 and /128</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">AllowedIps = 10.5.0.35/32, 2001:470:xxxx:xxxx::746f:786f/128</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Peer 2</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">[Peer]</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">PublicKey = xxx</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Which source IPs can I expect from that peer? This one has a LAN which can</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># access hosts/jails without NAT.</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Peer 2 has a single IP address inside the VPN: it&#x27;s 10.5.0.25/32</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">AllowedIps = 10.5.0.25/32,10.21.10.0/24,10.21.20.0/24,10.21.30.0/24,10.31.0.0/24,2001:470:xxxx:xxxx::ca:571e/128</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><h4><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="example-client-config"></a><strong>Example Client Config</strong><a class="hash-link" href="#example-client-config" title="Direct link to heading">#</a></h4><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI ini"><pre tabindex="0" class="prism-code language-ini codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain">[Interface]</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Which networks does my interface belong to? Notice: /24 and /64</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Address = 10.5.0.35/24, 2001:470:xxxx:xxxx::746f:786f/64</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">PrivateKey = xxx</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Server</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">[Peer]</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">PublicKey = xxx</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># I want to route everything through the server, both IPv4 and IPv6. All IPs are</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># thus available through the Server, and I can expect packets from any IP to</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># come from that peer.</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">AllowedIPs = 0.0.0.0/0, ::0/0</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Where is the server on the internet? This is a public address. The port</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># (:51820) is the same as ListenPort in the [Interface] of the Server file above</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Endpoint = 1.2.3.4:51820</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># Usually, clients are behind NAT. to keep the connection running, keep alive.</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">PersistentKeepalive = 15</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p>A useful tool for getting WG setup is <a href="https://github.com/trailofbits/algo" target="_blank" rel="noopener noreferrer">Algo</a>. It includes scripts and docs which cover almost all devices, platforms and clients, and has best practices implemented, and security features enabled. All of this is better explained in <a href="https://blog.trailofbits.com/2016/12/12/meet-algo-the-vpn-that-works/" target="_blank" rel="noopener noreferrer">this blog post</a>.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="reverse-ssh-tunnel"></a>Reverse SSH Tunnel<a class="hash-link" href="#reverse-ssh-tunnel" title="Direct link to heading">#</a></h3><p>SSH (or <a href="https://en.wikipedia.org/wiki/Secure_Shell" target="_blank" rel="noopener noreferrer">Secure Shell</a>) is a secure tunnel that allows you to connect to a remote host. Unlike the VPN methods, an SSH connection does not require an intermediary, and will not be affected by your IP changing. However it only allows you to access a single service at a time. SSH was really designed for terminal access, but because of the latter mentioned benefits it&#x27;s useful to setup, as a fallback option.</p><p>Directly SSH&#x27;ing into your home, would require you to open a port (usually 22), which would be terrible for security, and is not recommended. However a reverse SSH connection is initiated from inside your network. Once the connection is established, the port is redirected, allowing you to use the established connection to SSH into your home network.</p><p>The issue you&#x27;ve probably spotted, is that most public, corporate, and institutional networks will block SSH connections. To overcome this, you&#x27;d have to establish a server outside of your homelab that your homelab&#x27;s device could SSH into to establish the reverse SSH connection. You can then connect to that remote server (the <em>mothership</em>), which in turn connects to your home network.</p><p>Now all of this is starting to sound like quite a lot of work, but this is where services like <a href="https://remote.it/" target="_blank" rel="noopener noreferrer">remot3.it</a> come in. They maintain the intermediary mothership server, and create the tunnel service for you. It&#x27;s free for personal use, secure and easy. There are several similar services, such as <a href="https://remoteiot.com/" target="_blank" rel="noopener noreferrer">RemoteIoT</a>, or you could create your own on a cloud VPS (see <a href="https://gist.github.com/nileshtrivedi/4c615e8d3c1bf053b0d31176b9e69e42" target="_blank" rel="noopener noreferrer">this tutorial</a> for more info on that).</p><p>Before getting started, you&#x27;ll need to head over to <a href="https://app.remote.it/auth/#/sign-up" target="_blank" rel="noopener noreferrer">Remote.it</a> and create an account.</p><p>Then setup your local device:</p><ol><li>If you haven&#x27;t already done so, you&#x27;ll need to enable and configure SSH.<ul><li>This is out-of-scope of this article, but I&#x27;ve explained it in detail in <a href="https://notes.aliciasykes.com/22798/my-server-setup#configure-ssh" target="_blank" rel="noopener noreferrer">this post</a>.</li></ul></li><li>Download the Remote.it install script from their <a href="https://github.com/remoteit/installer" target="_blank" rel="noopener noreferrer">GitHub</a><ul><li><code>curl -LkO https://raw.githubusercontent.com/remoteit/installer/master/scripts/auto-install.sh</code></li></ul></li><li>Make it executable, with <code>chmod +x ./auto-install.sh</code>, and then run it with <code>sudo ./auto-install.sh</code></li><li>Finally, configure your device, by running <code>sudo connectd_installer</code> and following the on-screen instructions</li></ol><p>And when you&#x27;re ready to connect to it:</p><ol><li>Login to <a href="https://app.remote.it/" target="_blank" rel="noopener noreferrer">app.remote.it</a>, and select the name of your device</li><li>You should see a list of running services, click SSH</li><li>You&#x27;ll then be presented with some SSH credentials that you can now use to securely connect to your home, via the Remote.it servers</li></ol><p>Done :)</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="tcp-tunnel"></a>TCP Tunnel<a class="hash-link" href="#tcp-tunnel" title="Direct link to heading">#</a></h3><p>If you&#x27;re running Dashy on your local network, behind a firewall, but need to temporarily share it with someone external, this can be achieved quickly and securely using <a href="https://ngrok.com/" target="_blank" rel="noopener noreferrer">Ngrok</a>. It&#x27;s basically a super slick, encrypted TCP tunnel that provides an internet-accessible address that anyone use to access your local service, from anywhere.</p><p>To get started, <a href="https://ngrok.com/download" target="_blank" rel="noopener noreferrer">Download</a> and install Ngrok for your system, then just run <code>ngrok http [port]</code> (replace the port with the http port where Dashy is running, e.g. 8080). When <a href="https://ngrok.com/docs#http-local-https" target="_blank" rel="noopener noreferrer">using https</a>, specify the full local url/ ip including the protocol.</p><p>Some Ngrok features require you to be authenticated, you can <a href="https://dashboard.ngrok.com/signup" target="_blank" rel="noopener noreferrer">create a free account</a> and generate a token in <a href="https://dashboard.ngrok.com/auth/your-authtoken" target="_blank" rel="noopener noreferrer">your dashboard</a>, then run <code>ngrok authtoken [token]</code>.</p><p>It&#x27;s recommended to use authentication for any publicly accessible service. Dashy has an <a href="/docs/authentication">Auth</a> feature built in, but an even easier method it to use the <a href="https://ngrok.com/docs#http-auth" target="_blank" rel="noopener noreferrer"><code>-auth</code></a> switch. E.g. <code>ngrok http -auth=&quot;username:password123&quot; 8080</code></p><p>By default, your web app is assigned a randomly generated ngrok domain, but you can also use your own custom domain. Under the <a href="https://dashboard.ngrok.com/endpoints/domains" target="_blank" rel="noopener noreferrer">Domains Tab</a> of your Ngrok dashboard, add your domain, and follow the CNAME instructions. You can now use your domain, with the <a href="https://ngrok.com/docs#http-custom-domains" target="_blank" rel="noopener noreferrer"><code>-hostname</code></a> switch, e.g. <code>ngrok http -region=us -hostname=dashy.example.com 8080</code>. If you don&#x27;t have your own domain name, you can instead use a custom sub-domain (e.g. <code>alicia-dashy.ngrok.io</code>), using the <a href="https://ngrok.com/docs#custom-subdomain-names" target="_blank" rel="noopener noreferrer"><code>-subdomain</code></a> switch.</p><p>To integrate this into your docker-compose, take a look at the <a href="https://github.com/gtriggiano/ngrok-tunnel" target="_blank" rel="noopener noreferrer">gtriggiano/ngrok-tunnel</a> container.</p><p>There&#x27;s so much more you can do with Ngrok, such as exposing a directory as a file browser, using websockets, relaying requests, rewriting headers, inspecting traffic, TLS and TCP tunnels and lots more. All or which is explained in <a href="https://ngrok.com/docs" target="_blank" rel="noopener noreferrer">the Documentation</a>.</p><p>It&#x27;s worth noting that Ngrok isn&#x27;t the only option here, other options include: <a href="https://github.com/fatedier/frp" target="_blank" rel="noopener noreferrer">FRP</a>, <a href="https://inlets.dev" target="_blank" rel="noopener noreferrer">Inlets</a>, <a href="https://localtunnel.me/" target="_blank" rel="noopener noreferrer">Local Tunnel</a>, <a href="https://tailscale.com/" target="_blank" rel="noopener noreferrer">TailScale</a>, etc. Check out <a href="https://github.com/anderspitman/awesome-tunneling" target="_blank" rel="noopener noreferrer">Awesome Tunneling</a> for a list of alternatives.</p><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="custom-domain"></a>Custom Domain<a class="hash-link" href="#custom-domain" title="Direct link to heading">#</a></h2><ul><li><a href="#using-nginx">Using DNS</a></li><li><a href="#using-dns">Using NGINX</a></li></ul><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="using-dns"></a>Using DNS<a class="hash-link" href="#using-dns" title="Direct link to heading">#</a></h3><p>For locally running services, a domain can be set up directly in the DNS records. This method is really quick and easy, and doesn&#x27;t require you to purchase an actual domain. Just update your networks DNS resolver, to point your desired URL to the local IP where Dashy (or any other app) is running. For example, a line in your hosts file might look something like: <code>192.168.0.2 dashy.homelab.local</code>.</p><p>If you&#x27;re using Pi-Hole, a similar thing can be done in the <code>/etc/dnsmasq.d/03-custom-dns.conf</code> file, add a line like: <code>address=/dashy.example.com/192.168.2.0</code> for each of your services.</p><p>If you&#x27;re running OPNSense/ PfSense, then this can be done through the UI with Unbound, it&#x27;s explained nicely in <a href="https://homenetworkguy.com/how-to/use-custom-domain-name-in-internal-network/" target="_blank" rel="noopener noreferrer">this article</a>, by Dustin Casto.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="using-nginx"></a>Using NGINX<a class="hash-link" href="#using-nginx" title="Direct link to heading">#</a></h3><p>If you&#x27;re using NGINX, then you can use your own domain name, with a config similar to the below example.</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI text"><pre tabindex="0" class="prism-code language-text codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain">upstream dashy {</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> server 127.0.0.1:32400;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">server {</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> listen 80;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> server_name dashy.mydomain.com;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> # Setup SSL</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ssl_certificate /var/www/mydomain/sslcert.pem;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ssl_certificate_key /var/www/mydomain/sslkey.pem;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ssl_protocols TLSv1 TLSv1.1 TLSv1.2;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ssl_ciphers &#x27;EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH&#x27;;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ssl_session_timeout 5m;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ssl_prefer_server_ciphers on;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> location / {</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> proxy_pass http://dashy;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> proxy_redirect off;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> proxy_buffering off;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> proxy_set_header host $host;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> proxy_set_header X-Real-IP $remote_addr;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> }</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p>Similarly, a basic <code>Caddyfile</code> might look like:</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI text"><pre tabindex="0" class="prism-code language-text codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain">dashy.example.com {</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> reverse_proxy / nginx:80</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p>For more info, <a href="https://thehomelab.wiki/books/dns-reverse-proxy/page/create-domain-records-to-point-to-your-home-server-on-cloudflare-using-nginx-progy-manager" target="_blank" rel="noopener noreferrer">this guide</a> on Setting up Domains with NGINX Proxy Manager and CloudFlare may be useful.</p><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="container-security"></a>Container Security<a class="hash-link" href="#container-security" title="Direct link to heading">#</a></h2><ul><li><a href="#keep-docker-up-to-date">Keep Docker Up-To-Date</a></li><li><a href="#set-resource-quotas">Set Resource Quotas</a></li><li><a href="#dont-run-as-root">Don&#x27;t Run as Root</a></li><li><a href="#specify-a-user">Specify a User</a></li><li><a href="#limit-capabilities">Limit Capabilities</a></li><li><a href="#prevent-new-privileges-being-added">Prevent new Privileges being Added</a></li><li><a href="#disable-inter-container-communication">Disable Inter-Container Communication</a></li><li><a href="#dont-expose-the-docker-daemon-socket">Don&#x27;t Expose the Docker Daemon Socket</a></li><li><a href="#use-read-only-volumes">Use Read-Only Volumes</a></li><li><a href="#set-the-logging-level">Set the Logging Level</a></li><li><a href="#verify-image-before-pulling">Verify Image before Pulling</a></li><li><a href="#specify-the-tag">Specify the Tag</a></li><li><a href="#container-security-scanning">Container Security Scanning</a></li><li><a href="#registry-security">Registry Security</a></li><li><a href="#security-modules">Security Modules</a></li></ul><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="keep-docker-up-to-date"></a>Keep Docker Up-To-Date<a class="hash-link" href="#keep-docker-up-to-date" title="Direct link to heading">#</a></h3><p>To prevent known container escape vulnerabilities, which typically end in escalating to root/administrator privileges, patching Docker Engine and Docker Machine is crucial. For more info, see the <a href="https://docs.docker.com/engine/install/" target="_blank" rel="noopener noreferrer">Docker Installation Docs</a>.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="set-resource-quotas"></a>Set Resource Quotas<a class="hash-link" href="#set-resource-quotas" title="Direct link to heading">#</a></h3><p>Docker enables you to limit resource consumption (CPU, memory, disk) on a per-container basis. This not only enhances system performance, but also prevents a compromised container from consuming a large amount of resources, in order to disrupt service or perform malicious activities. To learn more, see the <a href="https://docs.docker.com/config/containers/resource_constraints/" target="_blank" rel="noopener noreferrer">Resource Constraints Docs</a></p><p>For example, to run Dashy with max of 1GB ram, and max of 50% of 1 CP core:
<code>docker run -d -p 8080:80 --cpus=&quot;.5&quot; --memory=&quot;1024m&quot; lissy93/dashy:latest</code></p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="dont-run-as-root"></a>Don&#x27;t Run as Root<a class="hash-link" href="#dont-run-as-root" title="Direct link to heading">#</a></h3><p>Running a container with admin privileges gives it more power than it needs, and can be abused. Dashy does not need any root privileges, and Docker by default doesn&#x27;t run containers as root, so providing you don&#x27;t specifically type sudo, you should be all good here.</p><p>Note that if you&#x27;re facing permission issues on Debian-based systems, you may need to add your user to the Docker group. First create the group: <code>sudo groupadd docker</code>, then add your (non-root) user: <code>sudo usermod aG docker [my-username]</code>, finally <code>newgrp docker</code> to refresh.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="specify-a-user"></a>Specify a User<a class="hash-link" href="#specify-a-user" title="Direct link to heading">#</a></h3><p>One of the best ways to prevent privilege escalation attacks, is to configure the container to use an unprivileged user. This also means that any files created by the container and mounted, will be owned by the specified user (and not root), which makes things much easier.</p><p>You can specify a user, using the <a href="https://docs.docker.com/engine/reference/run/#user" target="_blank" rel="noopener noreferrer"><code>--user</code> param</a>, and should include the user ID (<code>UID</code>), which can be found by running <code>id -u</code>, and the and the group ID (<code>GID</code>), using <code>id -g</code>.</p><p>With Docker run, you specify it like:
<code>docker run --user 1000:1000 -p 8080:80 lissy93/dashy</code></p><p>Of if you&#x27;re using Docker-compose, you could use an environmental variable</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI yaml"><pre tabindex="0" class="prism-code language-yaml codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token key atrule">version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">&quot;3.8&quot;</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">services</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">dashy</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> lissy93/dashy</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">user</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">CURRENT_UID</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">ports</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> 4000</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token number">80</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p>And then to set the variable, and start the container, run: <code>CURRENT_UID=$(id -u):$(id -g) docker-compose up</code></p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="limit-capabilities"></a>Limit capabilities<a class="hash-link" href="#limit-capabilities" title="Direct link to heading">#</a></h3><p>Docker containers run with a subset of <a href="https://man7.org/linux/man-pages/man7/capabilities.7.html" target="_blank" rel="noopener noreferrer">Linux Kernal&#x27;s Capabilities</a> by default. It&#x27;s good practice to drop privilege permissions that are not needed for any given container.</p><p>With Docker run, you can use the <code>--cap-drop</code> flag to remove capabilities, you can also use <code>--cap-drop=all</code> and then define just the required permissions using the <code>--cap-add</code> option. For a list of available capabilities, see the <a href="https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities" target="_blank" rel="noopener noreferrer">Privilege Capabilities Docs</a>.</p><p>Note that dropping privileges and capabilities on runtime is not fool-proof, and often any leftover privileges can be used to re-escalate, see <a href="https://wiki.sei.cmu.edu/confluence/display/c/POS36-C.+Observe+correct+revocation+order+while+relinquishing+privileges" target="_blank" rel="noopener noreferrer">POS36-C</a>.</p><p>Here&#x27;s an example using docker-compose, removing privileges that are not required for Dashy to run:</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI yaml"><pre tabindex="0" class="prism-code language-yaml codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token key atrule">version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">&quot;3.8&quot;</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">services</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">dashy</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> lissy93/dashy</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">ports</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> 4000</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token number">80</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">cap_drop</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> ALL</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token key atrule">cap_add</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> CHOWN</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> SETGID</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> SETUID</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> DAC_OVERRIDE</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> NET_BIND_SERVICE</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="prevent-new-privileges-being-added"></a>Prevent new Privileges being Added<a class="hash-link" href="#prevent-new-privileges-being-added" title="Direct link to heading">#</a></h3><p>To prevent processes inside the container from getting additional privileges, pass in the <code>--security-opt=no-new-privileges:true</code> option to the Docker run command (see <a href="https://docs.docker.com/engine/reference/run/#security-configuration" target="_blank" rel="noopener noreferrer">docs</a>).</p><p>Run Command:
<code>docker run --security-opt=no-new-privileges:true -p 8080:80 lissy93/dashy</code></p><p>Docker Compose</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI yaml"><pre tabindex="0" class="prism-code language-yaml codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token key atrule">security_opt</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> no</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">new</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">privileges</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token boolean important">true</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="disable-inter-container-communication"></a>Disable Inter-Container Communication<a class="hash-link" href="#disable-inter-container-communication" title="Direct link to heading">#</a></h3><p>By default Docker containers can talk to each other (using <a href="https://docs.docker.com/config/containers/container-networking/" target="_blank" rel="noopener noreferrer"><code>docker0</code> bridged network</a>). If you don&#x27;t need this capability, then it should be disabled. This can be done with the <code>--icc=false</code> in your run command. You can learn more about how to facilitate secure communication between containers in the <a href="https://docs.docker.com/compose/networking/" target="_blank" rel="noopener noreferrer">Compose Networking docs</a>.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="dont-expose-the-docker-daemon-socket"></a>Don&#x27;t Expose the Docker Daemon Socket<a class="hash-link" href="#dont-expose-the-docker-daemon-socket" title="Direct link to heading">#</a></h3><p>Docker socket <code>/var/run/docker.sock</code> is the UNIX socket that Docker is listening to. This is the primary entry point for the Docker API. The owner of this socket is root. Giving someone access to it is equivalent to giving unrestricted root access to your host.</p><p>You should <strong>not</strong> enable TCP Docker daemon socket (<code>-H tcp://0.0.0.0:XXX</code>), as doing so exposes un-encrypted and unauthenticated direct access to the Docker daemon, and if the host is connected to the internet, the daemon on your computer can be used by anyone from the public internet- which is bad. If you need TCP, you should <a href="https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-socket-option" target="_blank" rel="noopener noreferrer">see the docs</a> to understand how to do this more securely.
Similarly, never expose <code>/var/run/docker.sock</code> to other containers as a volume, as it can be exploited.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="use-read-only-volumes"></a>Use Read-Only Volumes<a class="hash-link" href="#use-read-only-volumes" title="Direct link to heading">#</a></h3><p>You can specify that a specific volume should be read-only by appending <code>:ro</code> to the <code>-v</code> switch. For example, while running Dashy, if we want our config to be writable, but keep all other assets protected, we would do:</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI bash"><pre tabindex="0" class="prism-code language-bash codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">docker</span><span class="token plain"> run -d </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> -p </span><span class="token number">8080</span><span class="token plain">:80 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> -v ~/dashy-conf.yml:/app/public/conf.yml </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> -v ~/dashy-icons:/app/public/item-icons:ro </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> -v ~/dashy-theme.scss:/app/src/styles/user-defined-themes.scss:ro </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> lissy93/dashy:latest</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p>You can also prevent a container from writing any changes to volumes on your host&#x27;s disk, using the <code>--read-only</code> flag. Although, for Dashy, you will not be able to write config changes to disk, when edited through the UI with this method. You could make this work, by specifying the config directory as a temp write location, with <code>--tmpfs /app/public/conf.yml</code> - but that this will not write the volume back to your host.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="set-the-logging-level"></a>Set the Logging Level<a class="hash-link" href="#set-the-logging-level" title="Direct link to heading">#</a></h3><p>Logging is important, as it enables you to review events in the future, and in the case of a compromise this will let get an idea of what may have happened. The default log level is <code>INFO</code>, and this is also the recommendation, use <code>--log-level info</code> to ensure this is set.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="verify-image-before-pulling"></a>Verify Image before Pulling<a class="hash-link" href="#verify-image-before-pulling" title="Direct link to heading">#</a></h3><p>Only use trusted images, from verified/ official sources. If an app is open source, it is more likely to be safe, as anyone can verify the code. There are also tools available for scanning containers,</p><p>Unless otherwise configured, containers can communicate among each other, so running one bad image may lead to other areas of your setup being compromised. Docker images typically contain both original code, as well as up-stream packages, and even if that image has come from a trusted source, the up-stream packages it includes may not have.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="specify-the-tag"></a>Specify the Tag<a class="hash-link" href="#specify-the-tag" title="Direct link to heading">#</a></h3><p>Using fixed tags (as opposed to <code>:latest</code> ) will ensure immutability, meaning the base image will not change between builds. Note that for Dashy, the app is being actively developed, new features, bug fixes and general improvements are merged each week, and if you use a fixed version you will not enjoy these benefits. So it&#x27;s up to you weather you would prefer a stable and reproducible environment, or the latest features and enhancements.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="container-security-scanning"></a>Container Security Scanning<a class="hash-link" href="#container-security-scanning" title="Direct link to heading">#</a></h3><p>It&#x27;s helpful to be aware of any potential security issues in any of the Docker images you are using. You can run a quick scan using Snyk on any image to output known vulnerabilities using <a href="https://docs.docker.com/engine/scan/" target="_blank" rel="noopener noreferrer">Docker scan</a>, e.g: <code>docker scan lissy93/dashy:latest</code>.</p><p>A similar product is <a href="https://github.com/aquasecurity/trivy" target="_blank" rel="noopener noreferrer">Trivy</a>, which is free an open source. First install it (with your package manager), then to scan an image, just run: <code>trivy image lissy93/dashy:latest</code></p><p>For larger systems, RedHat <a href="https://www.redhat.com/en/topics/containers/what-is-clair" target="_blank" rel="noopener noreferrer">Clair</a> is an app for parsing image contents and reporting on any found vulnerabilities. You run it locally in a container, and configure it with YAML. It can be integrated with Red Hat Quay, to show results on a dashboard. Most of these use static analysis to find potential issues, and scan included packages for any known security vulnerabilities.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="registry-security"></a>Registry Security<a class="hash-link" href="#registry-security" title="Direct link to heading">#</a></h3><p>Although over-kill for most users, you could run your own registry locally which would give you ultimate control over all images, see the <a href="https://docs.docker.com/registry/deploying/" target="_blank" rel="noopener noreferrer">Deploying a Registry Docs</a> for more info. Another option is <a href="https://docker-docs.netlify.app/ee/dtr/" target="_blank" rel="noopener noreferrer">Docker Trusted Registry</a>, it&#x27;s great for enterprise applications, it sits behind your firewall, running on a swarm managed by Docker Universal Control Plane, and lets you securely store and manage your Docker images, mitigating the risk of breaches from the internet.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="security-modules"></a>Security Modules<a class="hash-link" href="#security-modules" title="Direct link to heading">#</a></h3><p>Docker supports several modules that let you write your own security profiles.</p><p><a href="https://www.apparmor.net/" target="_blank" rel="noopener noreferrer">AppArmor</a>is a kernel module that proactively protects the operating system and applications from external or internal threats, by enabling you to restrict programs&#x27; capabilities with per-program profiles. You can specify either a security policy by name, or by file path with the <code>apparmor</code> flag in docker run. Learn more about writing profiles, <a href="https://gitlab.com/apparmor/apparmor/-/wikis/QuickProfileLanguage" target="_blank" rel="noopener noreferrer">here</a>.</p><p><a href="https://en.wikipedia.org/wiki/Seccomp" target="_blank" rel="noopener noreferrer">Seccomp</a> (Secure Computing Mode) is a sandboxing facility in the Linux kernel that acts like a firewall for system calls (syscalls). It uses Berkeley Packet Filter (BPF) rules to filter syscalls and control how they are handled. These filters can significantly limit a containers access to the Docker Host&#x27;s Linux kernel - especially for simple containers/applications. It requires a Linux-based Docker host, with secomp enabled, and you can check for this by running <code>docker info | grep seccomp</code>. A great resource for learning more about this is <a href="https://training.play-with-docker.com/security-seccomp/" target="_blank" rel="noopener noreferrer">DockerLabs</a>.</p><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="web-server-configuration"></a>Web Server Configuration<a class="hash-link" href="#web-server-configuration" title="Direct link to heading">#</a></h2><blockquote><p><em>The following section only applies if you are not using Docker, and would like to use your own web server</em></p></blockquote><p>Dashy ships with a pre-configured Node.js server, in <a href="https://github.com/Lissy93/dashy/blob/master/server.js" target="_blank" rel="noopener noreferrer"><code>server.js</code></a> which serves up the contents of the <code>./dist</code> directory on a given port. You can start the server by running <code>node server</code>. Note that the app must have been build (run <code>yarn build</code>), and you need <a href="https://nodejs.org" target="_blank" rel="noopener noreferrer">Node.js</a> installed.</p><p>If you wish to run Dashy from a sub page (e.g. <code>example.com/dashy</code>), then just set the <code>BASE_URL</code> environmental variable to that page name (in this example, <code>/dashy</code>), before building the app, and the path to all assets will then resolve to the new path, instead of <code>./</code>.</p><p>However, since Dashy is just a static web application, it can be served with whatever server you like. The following section outlines how you can configure a web server.</p><p>Note, that if you choose not to use <code>server.js</code> to serve up the app, you will loose access to the following features:</p><ul><li>Loading page, while the app is building</li><li>Writing config file to disk from the UI</li><li>Website status indicators, and ping checks</li></ul><p>Example Configs</p><ul><li><a href="#nginx">NGINX</a></li><li><a href="#apache">Apache</a></li><li><a href="#caddy">Caddy</a></li><li><a href="#firebase-hosting">Firebase</a></li><li><a href="#cpanel">cPanel</a></li></ul><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="nginx-1"></a>NGINX<a class="hash-link" href="#nginx-1" title="Direct link to heading">#</a></h3><p>Create a new file in <code>/etc/nginx/sites-enabled/dashy</code></p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI text"><pre tabindex="0" class="prism-code language-text codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain">server {</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> listen 80;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> listen [::]:80;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> root /var/www/dashy/html;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> index index.html;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> server_name your-domain.com www.your-domain.com;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> location / {</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> try_files $uri $uri/ =404;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> }</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p>To use HTML5 history mode (<code>appConfig.routingMode: history</code>), replace the inside of the location block with: <code>try_files $uri $uri/ /index.html;</code>.</p><p>Then upload the build contents of Dashy&#x27;s dist directory to that location.
For example: <code>scp -r ./dist/* [username]@[server_ip]:/var/www/dashy/html</code></p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="apache-1"></a>Apache<a class="hash-link" href="#apache-1" title="Direct link to heading">#</a></h3><p>Copy Dashy&#x27;s dist folder to your apache server, <code>sudo cp -r ./dashy/dist /var/www/html/dashy</code>.</p><p>In your Apache config, <code>/etc/apche2/apache2.conf</code> add:</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI text"><pre tabindex="0" class="prism-code language-text codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain">&lt;Directory /var/www/html&gt;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Options Indexes FollowSymLinks</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> AllowOverride All</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Require all granted</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">&lt;/Directory&gt;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">&lt;IfModule mod_rewrite.c&gt;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> RewriteEngine On</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> RewriteBase /</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> RewriteRule ^index\.html$ - [L]</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> RewriteCond %{REQUEST_FILENAME} !-f</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> RewriteCond %{REQUEST_FILENAME} !-d</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> RewriteRule . /index.html [L]</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">&lt;/IfModule&gt;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p>Add a <code>.htaccess</code> file within <code>/var/www/html/dashy/.htaccess</code>, and add:</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI text"><pre tabindex="0" class="prism-code language-text codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain">Options -MultiViews</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">RewriteEngine On</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">RewriteCond %{REQUEST_FILENAME} !-f</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">RewriteRule ^ index.html [QSA,L]</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p>Then restart Apache, with <code>sudo systemctl restart apache2</code></p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="caddy-1"></a>Caddy<a class="hash-link" href="#caddy-1" title="Direct link to heading">#</a></h3><p>Caddy v2</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI text"><pre tabindex="0" class="prism-code language-text codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain">try_files {path} /</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><p>Caddy v1</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI text"><pre tabindex="0" class="prism-code language-text codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain">rewrite {</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> regexp .*</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> to {path} /</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="firebase-hosting"></a>Firebase Hosting<a class="hash-link" href="#firebase-hosting" title="Direct link to heading">#</a></h3><p>Create a file names <code>firebase.json</code>, and populate it with something similar to:</p><div class="codeBlockContainer_J+bg"><div class="codeBlockContent_csEI text"><pre tabindex="0" class="prism-code language-text codeBlock_rtdJ thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_1zSZ"><span class="token-line" style="color:#F8F8F2"><span class="token plain">{</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> &quot;hosting&quot;: {</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> &quot;public&quot;: &quot;dist&quot;,</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> &quot;rewrites&quot;: [</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> {</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> &quot;source&quot;: &quot;**&quot;,</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> &quot;destination&quot;: &quot;/index.html&quot;</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> }</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> ]</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> }</span></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_M3SB clean-btn">Copy</button></div></div><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="cpanel"></a>cPanel<a class="hash-link" href="#cpanel" title="Direct link to heading">#</a></h3><ol><li>Login to your WHM</li><li>Open &#x27;Feature Manager&#x27; on the left sidebar</li><li>Under &#x27;Manage feature list&#x27;, click &#x27;Edit&#x27;</li><li>Find &#x27;Application manager&#x27; in the list, enable it and hit &#x27;Save&#x27;</li><li>Log into your users cPanel account, and under &#x27;Software&#x27; find &#x27;Application Manager&#x27;</li><li>Click &#x27;Register Application&#x27;, fill in the form using the path that Dashy is located, and choose a domain, and hit &#x27;Save&#x27;</li><li>The application should now show up in the list, click &#x27;Ensure dependencies&#x27;, and move the toggle switch to &#x27;Enabled&#x27;</li><li>If you need to change the port, click &#x27;Add environmental variable&#x27;, give it the name &#x27;PORT&#x27;, choose a port number and press &#x27;Save&#x27;.</li><li>Dashy should now be running at your selected path an on a given port</li></ol><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="running-a-modified-version-of-the-app"></a>Running a Modified Version of the App<a class="hash-link" href="#running-a-modified-version-of-the-app" title="Direct link to heading">#</a></h2><p>If you&#x27;d like to make any code changes to the app, and deploy your modified version, this section briefly explains how.</p><p>The first step is to fork the project on GitHub, and clone it to your local system. Next, install the dependencies (<code>yarn</code>), and start the development server (<code>yarn dev</code>) and visit <code>localhost:8080</code> in your browser. You can then make changes to the codebase, and see the live app update in real-time. Once you&#x27;ve finished, running <code>yarn build</code> will build the app for production, and output the assets into <code>./dist</code> which can then be deployed using a web server, CDN or the built-in Node server with <code>yarn start</code>. For more info on all of this, take a look at the <a href="/docs/developing">Developing Docs</a>. To build your own Docker container from the modified app, see <a href="#building-your-own-container">Building your Own Container</a></p><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_WiXH" id="building-your-own-container"></a>Building your Own Container<a class="hash-link" href="#building-your-own-container" title="Direct link to heading">#</a></h2><p>Similar to above, you&#x27;ll first need to fork and clone Dashy to your local system, and then install dependencies.</p><p>Then, either use Dashy&#x27;s default <a href="https://github.com/Lissy93/dashy/blob/master/Dockerfile" target="_blank" rel="noopener noreferrer"><code>Dockerfile</code></a> as is, or modify it according to your needs.</p><p>To build and deploy locally, first build the app with: <code>docker build -t dashy .</code>, and then start the app with <code>docker run -p 8080:80 --name my-dashboard dashy</code>. Or modify the <code>docker-compose.yml</code> file, replacing <code>image: lissy93/dashy</code> with <code>build: .</code> and run <code>docker compose up</code>.</p><p>Your container should now be running, and will appear in the list when you run <code>docker container ls a</code>. If you&#x27;d like to enter the container, run <code>docker exec -it [container-id] /bin/ash</code>.</p><p>You may wish to upload your image to a container registry for easier access. Note that if you choose to do this on a public registry, please name your container something other than just &#x27;dashy&#x27;, to avoid confusion with the official image.
You can push your build image, by running: <code>docker push ghcr.io/OWNER/IMAGE_NAME:latest</code>. You will first need to authenticate, this can be done by running <code>echo $CR_PAT | docker login ghcr.io -u USERNAME --password-stdin</code>, where <code>CR_PAT</code> is an environmental variable containing a token generated from your GitHub account. For more info, see the <a href="https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry" target="_blank" rel="noopener noreferrer">Container Registry Docs</a>.</p><p><strong><a href="#top">⬆️ Back to Top</a></strong></p><hr></div><footer class="row docusaurus-mt-lg"><div class="col"><a href="https://github.com/Lissy93/dashy/edit/gh-pages/docs/docs/management.md" target="_blank" rel="noreferrer noopener"><svg fill="currentColor" height="20" width="20" viewBox="0 0 40 40" class="iconEdit_mS5F" aria-hidden="true"><g><path d="m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"></path></g></svg>Edit this page</a></div><div class="col lastUpdated_wj+Z"></div></footer></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Docs pages navigation"><div class="pagination-nav__item"><a class="pagination-nav__link" href="/docs/configuring"><div class="pagination-nav__sublabel">Previous</div><div class="pagination-nav__label">« Configuring</div></a></div><div class="pagination-nav__item pagination-nav__item--next"><a class="pagination-nav__link" href="/docs/troubleshooting"><div class="pagination-nav__sublabel">Next</div><div class="pagination-nav__label">Troubleshooting »</div></a></div></nav></div></div><div class="col col--3"><div class="tableOfContents_vrFS thin-scrollbar"><ul class="table-of-contents table-of-contents__left-border"><li><a href="#contents" class="table-of-contents__link">Contents</a></li><li><a href="#providing-assets" class="table-of-contents__link">Providing Assets</a></li><li><a href="#running-commands" class="table-of-contents__link">Running Commands</a></li><li><a href="#healthchecks" class="table-of-contents__link">Healthchecks</a></li><li><a href="#logs-and-performance" class="table-of-contents__link">Logs and Performance</a><ul><li><a href="#container-logs" class="table-of-contents__link">Container Logs</a></li><li><a href="#container-performance" class="table-of-contents__link">Container Performance</a></li><li><a href="#management-apps" class="table-of-contents__link">Management Apps</a></li><li><a href="#advanced-logging-and-monitoring" class="table-of-contents__link">Advanced Logging and Monitoring</a></li></ul></li><li><a href="#auto-starting-at-system-boot" class="table-of-contents__link">Auto-Starting at System Boot</a></li><li><a href="#updating" class="table-of-contents__link">Updating</a><ul><li><a href="#updating-docker-container" class="table-of-contents__link">Updating Docker Container</a></li><li><a href="#automatic-docker-updates" class="table-of-contents__link">Automatic Docker Updates</a></li><li><a href="#updating-dashy-from-source" class="table-of-contents__link">Updating Dashy from Source</a></li></ul></li><li><a href="#backing-up" class="table-of-contents__link">Backing Up</a><ul><li><a href="#backing-up-containers" class="table-of-contents__link">Backing Up Containers</a></li><li><a href="#backing-up-volumes" class="table-of-contents__link">Backing Up Volumes</a></li><li><a href="#dashy-specific-backup" class="table-of-contents__link">Dashy-Specific Backup</a></li></ul></li><li><a href="#scheduling" class="table-of-contents__link">Scheduling</a></li><li><a href="#ssl-certificates" class="table-of-contents__link">SSL Certificates</a><ul><li><a href="#auto-ssl" class="table-of-contents__link">Auto-SSL</a></li><li><a href="#getting-a-self-signed-ssl-certificate" class="table-of-contents__link">Getting a Self-Signed SSL Certificate</a></li><li><a href="#passing-a-self-signed-certificate-to-dashy" class="table-of-contents__link">Passing a Self-Signed Certificate to Dashy</a></li></ul></li><li><a href="#authentication" class="table-of-contents__link">Authentication</a></li><li><a href="#managing-containers-with-docker-compose" class="table-of-contents__link">Managing Containers with Docker Compose</a></li><li><a href="#passing-in-environmental-variables" class="table-of-contents__link">Passing in Environmental Variables</a></li><li><a href="#setting-headers" class="table-of-contents__link">Setting Headers</a><ul><li><a href="#example-headers" class="table-of-contents__link">Example Headers</a></li></ul></li><li><a href="#remote-access" class="table-of-contents__link">Remote Access</a><ul><li><a href="#wireguard" class="table-of-contents__link">WireGuard</a></li><li><a href="#reverse-ssh-tunnel" class="table-of-contents__link">Reverse SSH Tunnel</a></li><li><a href="#tcp-tunnel" class="table-of-contents__link">TCP Tunnel</a></li></ul></li><li><a href="#custom-domain" class="table-of-contents__link">Custom Domain</a><ul><li><a href="#using-dns" class="table-of-contents__link">Using DNS</a></li><li><a href="#using-nginx" class="table-of-contents__link">Using NGINX</a></li></ul></li><li><a href="#container-security" class="table-of-contents__link">Container Security</a><ul><li><a href="#keep-docker-up-to-date" class="table-of-contents__link">Keep Docker Up-To-Date</a></li><li><a href="#set-resource-quotas" class="table-of-contents__link">Set Resource Quotas</a></li><li><a href="#dont-run-as-root" class="table-of-contents__link">Don&#39;t Run as Root</a></li><li><a href="#specify-a-user" class="table-of-contents__link">Specify a User</a></li><li><a href="#limit-capabilities" class="table-of-contents__link">Limit capabilities</a></li><li><a href="#prevent-new-privileges-being-added" class="table-of-contents__link">Prevent new Privileges being Added</a></li><li><a href="#disable-inter-container-communication" class="table-of-contents__link">Disable Inter-Container Communication</a></li><li><a href="#dont-expose-the-docker-daemon-socket" class="table-of-contents__link">Don&#39;t Expose the Docker Daemon Socket</a></li><li><a href="#use-read-only-volumes" class="table-of-contents__link">Use Read-Only Volumes</a></li><li><a href="#set-the-logging-level" class="table-of-contents__link">Set the Logging Level</a></li><li><a href="#verify-image-before-pulling" class="table-of-contents__link">Verify Image before Pulling</a></li><li><a href="#specify-the-tag" class="table-of-contents__link">Specify the Tag</a></li><li><a href="#container-security-scanning" class="table-of-contents__link">Container Security Scanning</a></li><li><a href="#registry-security" class="table-of-contents__link">Registry Security</a></li><li><a href="#security-modules" class="table-of-contents__link">Security Modules</a></li></ul></li><li><a href="#web-server-configuration" class="table-of-contents__link">Web Server Configuration</a><ul><li><a href="#nginx-1" class="table-of-contents__link">NGINX</a></li><li><a href="#apache-1" class="table-of-contents__link">Apache</a></li><li><a href="#caddy-1" class="table-of-contents__link">Caddy</a></li><li><a href="#firebase-hosting" class="table-of-contents__link">Firebase Hosting</a></li><li><a href="#cpanel" class="table-of-contents__link">cPanel</a></li></ul></li><li><a href="#running-a-modified-version-of-the-app" class="table-of-contents__link">Running a Modified Version of the App</a></li><li><a href="#building-your-own-container" class="table-of-contents__link">Building your Own Container</a></li></ul></div></div></div></div></main></div></div><footer class="footer footer--dark"><div class="container"><div class="row footer__links"><div class="col footer__col"><div class="footer__title">Intro</div><ul class="footer__items"><li class="footer__item"><a href="https://github.com/lissy93/dashy" target="_blank" rel="noopener noreferrer" class="footer__link-item">GitHub</a></li><li class="footer__item"><a href="https://demo.dashy.to" target="_blank" rel="noopener noreferrer" class="footer__link-item">Live Demo</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/quick-start">Quick Start</a></li><li class="footer__item"><a class="footer__link-item" href="/docs">Documentation</a></li></ul></div><div class="col footer__col"><div class="footer__title">Setup Guide</div><ul class="footer__items"><li class="footer__item"><a class="footer__link-item" href="/docs/deployment">Deploying</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/configuring">Configuring</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/management">Management</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/troubleshooting">Troubleshooting</a></li></ul></div><div class="col footer__col"><div class="footer__title">Feature Docs Pt 1</div><ul class="footer__items"><li class="footer__item"><a class="footer__link-item" href="/docs/authentication">Authentication</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/alternate-views">Alternate Views</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/backup-restore">Backup &amp; Restore</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/icons">Icons</a></li></ul></div><div class="col footer__col"><div class="footer__title">Feature Docs Pt 2</div><ul class="footer__items"><li class="footer__item"><a class="footer__link-item" href="/docs/multi-language-support">Language Switching</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/status-indicators">Status Indicators</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/searching">Searching &amp; Shortcuts</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/theming">Theming</a></li></ul></div><div class="col footer__col"><div class="footer__title">Community</div><ul class="footer__items"><li class="footer__item"><a class="footer__link-item" href="/docs/developing">Developing</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/development-guides">Development Guides</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/contributing">Contributing</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/showcase">Showcase</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/credits">Credits</a></li></ul></div><div class="col footer__col"><div class="footer__title">Misc</div><ul class="footer__items"><li class="footer__item"><a class="footer__link-item" href="/docs/privacy">Privacy &amp; Security</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/license">License</a></li><li class="footer__item"><a href="https://github.com/Lissy93/dashy/blob/master/.github/LEGAL.md" target="_blank" rel="noopener noreferrer" class="footer__link-item">Legal</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/code-of-conduct">Code of Conduct</a></li><li class="footer__item"><a class="footer__link-item" href="/docs/changelog">Changelog</a></li></ul></div></div><div class="footer__bottom text--center"><div class="footer__copyright"><a href="https://dashy.to">Dashy</a> - The Self-Hosted Dashboard for your Homelab<br>License under <a href="https://github.com/Lissy93/dashy/blob/master/LICENSE">MIT</a>. Copyright © 2024 <a href="https://aliciasykes.com">Alicia Sykes</a></div></div></div></footer></div>
<script src="/assets/js/runtime~main.ac7349e8.js"></script>
<script src="/assets/js/main.d13b237a.js"></script>
</body>
</html>