Files
shields/tutorial-self-hosting.html
2025-03-14 19:47:18 +00:00

191 lines
16 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">
<head>
<meta charset="utf-8">
<title>JSDoc: Tutorial: self-hosting</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Tutorial: self-hosting</h1>
<section>
<header>
<h2>self-hosting</h2>
</header>
<article>
<h1>Hosting your own Shields server</h1>
<p>This document describes how to host your own shields server either from source or using a docker image. See the docs on <a href="https://github.com/badges/shields/blob/master/doc/releases.md#shields-server">releases</a> for info on how we version the server and how to choose a release.</p>
<h2>Installing from Source</h2>
<p>You will need Node 20 or later, which you can install using a
<a href="https://nodejs.org/en/download/package-manager/">package manager</a>.</p>
<p>On Ubuntu / Debian:</p>
<pre class="prettyprint source lang-sh"><code>curl -sL https://deb.nodesource.com/setup_20.x | sudo -E bash -; sudo apt-get install -y nodejs
</code></pre>
<pre class="prettyprint source lang-sh"><code>git clone https://github.com/badges/shields.git
cd shields
git checkout $(git tag | grep server | tail -n 1) # checkout the latest tag
npm ci # You may need sudo for this.
</code></pre>
<h3>Build the frontend</h3>
<pre class="prettyprint source lang-sh"><code>npm run build
</code></pre>
<h3>Start the server</h3>
<pre class="prettyprint source lang-sh"><code>sudo node server
</code></pre>
<p>The server uses port 80 by default, which requires <code>sudo</code> permissions.</p>
<p>There are two ways to provide an alternate port:</p>
<pre class="prettyprint source lang-sh"><code>PORT=8080 node server
node server 8080
</code></pre>
<p>The root gets redirected to https://shields.io.</p>
<p>For testing purposes, you can go to <code>http://localhost/</code>.</p>
<h3>Deploying to Heroku</h3>
<p>Once you have installed the <a href="https://devcenter.heroku.com/articles/heroku-cli">Heroku CLI</a></p>
<pre class="prettyprint source lang-bash"><code>heroku login
heroku create your-app-name
git push heroku master
heroku open
</code></pre>
<h3>Deploying to Zeit Vercel</h3>
<p>To deploy using Zeit Vercel:</p>
<pre class="prettyprint source lang-console"><code>npm run build # Not sure why, but this needs to be run before deploying.
vercel
</code></pre>
<h2>Docker</h2>
<h3>Public Images</h3>
<p>We publish images to:</p>
<ul>
<li>DockerHub at https://registry.hub.docker.com/r/shieldsio/shields and</li>
<li>GitHub Container Registry at https://github.com/badges/shields/pkgs/container/shields</li>
</ul>
<p>The <code>next</code> tag is the latest build from <code>master</code>. These are only available for linux/amd64</p>
<pre class="prettyprint source lang-sh"><code># DockerHub
$ docker pull shieldsio/shields:next
$ docker run shieldsio/shields:next
</code></pre>
<pre class="prettyprint source lang-sh"><code># GHCR
$ docker pull ghcr.io/badges/shields:next
$ docker pull ghcr.io/badges/shields:next
</code></pre>
<p>Tagged snapshot releases are also available:</p>
<ul>
<li>https://registry.hub.docker.com/r/shieldsio/shields/tags</li>
<li>https://github.com/badges/shields/pkgs/container/shields/versions?filters%5Bversion_type%5D=tagged</li>
</ul>
<p>We push both linux/amd64 and linux/arm64 snapshot images. We use the linux/amd64 image ourselves to host shields.io. We push a linux/arm64 image, but we don't consume it ourselves and it receives no testing beyond ensuring the docker image builds without error.</p>
<h3>Building Docker Image Locally</h3>
<p>Alternatively, you can build and run the server locally using Docker. First build an image:</p>
<pre class="prettyprint source lang-console"><code>$ docker build -t shields .
Sending build context to Docker daemon 3.923 MB
Successfully built 4471b442c220
</code></pre>
<p>Optionally, alter the default values for configuration by setting them via <a href="https://docs.docker.com/engine/reference/commandline/run/#set-environment-variables--e---env---env-file">environment variables</a>.
See <a href="server-secrets.md">server-secrets.md</a> and <a href="/config/custom-environment-variables.yml">config/custom-environment-variables.yml</a> for possible values.
In <a href="/config/custom-environment-variables.yml">config/custom-environment-variables.yml</a>, environment variable names are specified as the quoted, uppercase key values (e.g. <code>GH_TOKEN</code>).</p>
<p>Then run the container, and be sure to specify the same mapped port as the one Shields is listening on :</p>
<pre class="prettyprint source lang-console"><code>$ docker run --rm -p 8080:8080 --env PORT=8080 --name shields shieldsio/shields:next
Configuration:
...
0916211515 Server is starting up: http://0.0.0.0:8080/
</code></pre>
<p>Assuming Docker is running locally, you should be able to get to the
application at http://localhost:8080/.</p>
<p>If you run Docker in a virtual machine (such as boot2docker or Docker Machine)
then you will need to replace <code>localhost</code> with the IP address of that virtual
machine.</p>
<h2>Raster server</h2>
<p>If you want to host PNG badges, you can also self-host a <a href="https://github.com/badges/squint">raster server</a>
which points to your badge server. It's a docker container. We host it on
Fly.io but should be possible to host on a wide variety of platforms.</p>
<ul>
<li>In your raster instance, set <code>BASE_URL</code> to your Shields instance, e.g.
<code>https://shields.example.co</code>.</li>
<li>Optionally, in your Shields, instance, configure <code>RASTER_URL</code> to the base
URL, e.g. <code>https://raster.example.co</code>. This will send 301 redirects
for the legacy raster URLs instead of 404's.</li>
</ul>
<p>If anyone has set this up, more documentation on how to do this would be
welcome!</p>
<h2>Server secrets</h2>
<p>You can add your own server secrets in environment variables or <code>config/local.yml</code>.</p>
<p>These are documented in <a href="./server-secrets.md">server-secrets.md</a></p>
<h2>Separate frontend hosting</h2>
<p>If you want to host the frontend on a separate server, such as cloud storage
or a CDN, you can do that.</p>
<p>First, build the frontend, pointing <code>BASE_URL</code> to your server.</p>
<pre class="prettyprint source lang-sh"><code>BASE_URL=https://your-server.example.com npm run build
</code></pre>
<p>Then copy the contents of the <code>public/</code> folder to your static hosting / CDN.</p>
<p>There are also a couple settings you should configure on the server.</p>
<p>To help out users, you can make the Shields server redirect the server root.
Set the <code>REDIRECT_URI</code> environment variable:</p>
<pre class="prettyprint source lang-sh"><code>REDIRECT_URI=http://my-custom-shields.s3.amazonaws.com/
</code></pre>
<h2>Sentry</h2>
<p>In order to enable integration with <a href="https://sentry.io">Sentry</a>, you need your own <a href="https://docs.sentry.io/quickstart/#configure-the-dsn">Sentry DSN</a>. Its an URL in format <code>https://{PUBLIC_KEY}:{SECRET_KEY}@sentry.io/{PROJECT_ID}</code>.</p>
<h3>How to obtain the Sentry DSN</h3>
<ol>
<li><a href="https://sentry.io/pricing/">Sign up</a> for Sentry</li>
<li>Log in to Sentry</li>
<li>Create a new project for Node.js</li>
<li>You should see <a href="https://docs.sentry.io/quickstart/#configure-the-dsn">Sentry DSN</a> for your project. Sentry DSN can be found by navigating to [Project Name] -&gt; Project Settings -&gt; Client Keys (DSN) as well.</li>
</ol>
<p>Start the server using the Sentry DSN. You can set it:</p>
<ul>
<li>by <code>SENTRY_DSN</code> environment variable</li>
</ul>
<pre class="prettyprint source"><code>sudo SENTRY_DSN=https://xxx:yyy@sentry.io/zzz node server
</code></pre>
<p>Or via config as you would do with <a href="server-secrets.md">server secrets</a>:</p>
<pre class="prettyprint source lang-yml"><code>private:
sentry_dsn: ...
</code></pre>
<pre class="prettyprint source lang-sh"><code>sudo node server
</code></pre>
<h2>Prometheus</h2>
<p>Shields uses <a href="https://github.com/siimon/prom-client">prom-client</a> to provide <a href="https://prometheus.io/docs/instrumenting/writing_clientlibs/#standard-and-runtime-collectors">default metrics</a>. These metrics are disabled by default.
You can enable them by <code>METRICS_PROMETHEUS_ENABLED</code> and <code>METRICS_PROMETHEUS_ENDPOINT_ENABLED</code> environment variables.</p>
<pre class="prettyprint source lang-bash"><code>METRICS_PROMETHEUS_ENABLED=true METRICS_PROMETHEUS_ENDPOINT_ENABLED=true npm start
</code></pre>
<p>Metrics are available at <code>/metrics</code> resource.</p>
<h2>Cloudflare</h2>
<p>Shields.io uses Cloudflare as a downstream CDN. If your installation does the same,
you can configure your server to only accept requests coming from Cloudflare's IPs.
Set <code>public.requireCloudflare: true</code>.</p>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-badge-maker.html">badge-maker</a></li><li><a href="module-badge-maker_lib_xml.html">badge-maker/lib/xml</a></li><li><a href="module-core_base-service_base.html">core/base-service/base</a></li><li><a href="module-core_base-service_base-graphql.html">core/base-service/base-graphql</a></li><li><a href="module-core_base-service_base-json.html">core/base-service/base-json</a></li><li><a href="module-core_base-service_base-svg-scraping.html">core/base-service/base-svg-scraping</a></li><li><a href="module-core_base-service_base-toml.html">core/base-service/base-toml</a></li><li><a href="module-core_base-service_base-xml.html">core/base-service/base-xml</a></li><li><a href="module-core_base-service_base-yaml.html">core/base-service/base-yaml</a></li><li><a href="module-core_base-service_errors.html">core/base-service/errors</a></li><li><a href="module-core_base-service_graphql.html">core/base-service/graphql</a></li><li><a href="module-core_base-service_openapi.html">core/base-service/openapi</a></li><li><a href="module-core_base-service_resource-cache.html">core/base-service/resource-cache</a></li><li><a href="module-core_base-service_service-definitions.html">core/base-service/service-definitions</a></li><li><a href="module-core_server_server.html">core/server/server</a></li><li><a href="module-core_service-test-runner_create-service-tester.html">core/service-test-runner/create-service-tester</a></li><li><a href="module-core_service-test-runner_icedfrisby-shields.html">core/service-test-runner/icedfrisby-shields</a></li><li><a href="module-core_service-test-runner_runner.html">core/service-test-runner/runner</a></li><li><a href="module-core_service-test-runner_service-tester.html">core/service-test-runner/service-tester</a></li><li><a href="module-core_service-test-runner_services-for-title.html">core/service-test-runner/services-for-title</a></li><li><a href="module-core_token-pooling_token-pool.html">core/token-pooling/token-pool</a></li><li><a href="module-services_build-status.html">services/build-status</a></li><li><a href="module-services_color-formatters.html">services/color-formatters</a></li><li><a href="module-services_contributor-count.html">services/contributor-count</a></li><li><a href="module-services_date.html">services/date</a></li><li><a href="module-services_downloads.html">services/downloads</a></li><li><a href="module-services_dynamic-common.html">services/dynamic-common</a></li><li><a href="module-services_dynamic_json-path.html">services/dynamic/json-path</a></li><li><a href="module-services_endpoint-common.html">services/endpoint-common</a></li><li><a href="module-services_licenses.html">services/licenses</a></li><li><a href="module-services_package-json-helpers.html">services/package-json-helpers</a></li><li><a href="module-services_php-version.html">services/php-version</a></li><li><a href="module-services_pipenv-helpers.html">services/pipenv-helpers</a></li><li><a href="module-services_route-builder.html">services/route-builder</a></li><li><a href="module-services_size.html">services/size</a></li><li><a href="module-services_steam_steam-base.html">services/steam/steam-base</a></li><li><a href="module-services_text-formatters.html">services/text-formatters</a></li><li><a href="module-services_validators.html">services/validators</a></li><li><a href="module-services_version.html">services/version</a></li><li><a href="module-services_website-status.html">services/website-status</a></li><li><a href="module-services_winget_version.html">services/winget/version</a></li></ul><h3>Classes</h3><ul><li><a href="BaseThunderstoreService.html">BaseThunderstoreService</a></li><li><a href="module-badge-maker_lib_xml-ElementList.html">ElementList</a></li><li><a href="module-badge-maker_lib_xml-XmlElement.html">XmlElement</a></li><li><a href="module-core_base-service_base-graphql-BaseGraphqlService.html">BaseGraphqlService</a></li><li><a href="module-core_base-service_base-json-BaseJsonService.html">BaseJsonService</a></li><li><a href="module-core_base-service_base-svg-scraping-BaseSvgScrapingService.html">BaseSvgScrapingService</a></li><li><a href="module-core_base-service_base-toml-BaseTomlService.html">BaseTomlService</a></li><li><a href="module-core_base-service_base-xml-BaseXmlService.html">BaseXmlService</a></li><li><a href="module-core_base-service_base-yaml-BaseYamlService.html">BaseYamlService</a></li><li><a href="module-core_base-service_base-BaseService.html">BaseService</a></li><li><a href="module-core_base-service_errors-Deprecated.html">Deprecated</a></li><li><a href="module-core_base-service_errors-ImproperlyConfigured.html">ImproperlyConfigured</a></li><li><a href="module-core_base-service_errors-Inaccessible.html">Inaccessible</a></li><li><a href="module-core_base-service_errors-InvalidParameter.html">InvalidParameter</a></li><li><a href="module-core_base-service_errors-InvalidResponse.html">InvalidResponse</a></li><li><a href="module-core_base-service_errors-NotFound.html">NotFound</a></li><li><a href="module-core_base-service_errors-ShieldsRuntimeError.html">ShieldsRuntimeError</a></li><li><a href="module-core_server_server-Server.html">Server</a></li><li><a href="module-core_service-test-runner_runner-Runner.html">Runner</a></li><li><a href="module-core_service-test-runner_service-tester-ServiceTester.html">ServiceTester</a></li><li><a href="module-core_token-pooling_token-pool-Token.html">Token</a></li><li><a href="module-core_token-pooling_token-pool-TokenPool.html">TokenPool</a></li><li><a href="module-services_route-builder.html">services/route-builder</a></li><li><a href="module-services_steam_steam-base-BaseSteamAPI.html">BaseSteamAPI</a></li></ul><h3>Tutorials</h3><ul><li><a href="tutorial-TUTORIAL.html">TUTORIAL</a></li><li><a href="tutorial-adding-new-config-values.html">adding-new-config-values</a></li><li><a href="tutorial-authentication.html">authentication</a></li><li><a href="tutorial-badge-urls.html">badge-urls</a></li><li><a href="tutorial-code-walkthrough.html">code-walkthrough</a></li><li><a href="tutorial-deprecating-badges.html">deprecating-badges</a></li><li><a href="tutorial-input-validation.html">input-validation</a></li><li><a href="tutorial-json-format.html">json-format</a></li><li><a href="tutorial-performance-testing.html">performance-testing</a></li><li><a href="tutorial-production-hosting.html">production-hosting</a></li><li><a href="tutorial-releases.html">releases</a></li><li><a href="tutorial-self-hosting.html">self-hosting</a></li><li><a href="tutorial-server-secrets.html">server-secrets</a></li><li><a href="tutorial-service-tests.html">service-tests</a></li><li><a href="tutorial-static-badges.html">static-badges</a></li></ul><h3>Global</h3><ul><li><a href="global.html#createNumRequestCounter">createNumRequestCounter</a></li><li><a href="global.html#fakeJwtToken">fakeJwtToken</a></li><li><a href="global.html#generateFakeConfig">generateFakeConfig</a></li><li><a href="global.html#getBadgeExampleCall">getBadgeExampleCall</a></li><li><a href="global.html#getServiceClassAuthOrigin">getServiceClassAuthOrigin</a></li><li><a href="global.html#isMetricWithPattern">isMetricWithPattern</a></li><li><a href="global.html#testAuth">testAuth</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Fri Mar 14 2025 19:47:18 GMT+0000 (Coordinated Universal Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>