1"use strict";(globalThis.webpackChunkdrand_docs=globalThis.webpackChunkdrand_docs||[]).push([[9698],{99058(e,n,r){r.r(n),r.d(n,{assets:()=>c,contentTitle:()=>i,default:()=>h,frontMatter:()=>d,metadata:()=>o,toc:()=>a});const o=JSON.parse('{"id":"ops-guide/ops-docker-install","title":"Docker Install","description":"Learn how to install drand using Docker","source":"@site/docs/ops-guide/02-Docker_Install.md","sourceDirName":"ops-guide","slug":"/operator/docker/","permalink":"/operator/docker/","draft":false,"unlisted":false,"editUrl":"https://github.com/drand/drand-docs/tree/master/docs/ops-guide/02-Docker_Install.md","tags":[],"version":"current","sidebarPosition":2,"frontMatter":{"id":"ops-docker-install","title":"Docker Install","description":"Learn how to install drand using Docker","slug":"/operator/docker/"},"sidebar":"opsSidebar","previous":{"title":"Deployment","permalink":"/operator/deploy/"},"next":{"title":"Storage Backends","permalink":"/operator/storage-backends/"}}');var s=r(74848),t=r(28453);const d={id:"ops-docker-install",title:"Docker Install",description:"Learn how to install drand using Docker",slug:"/operator/docker/"},i="Docker Install",c={},a=[{value:"<strong>Prerequisites for this Guide</strong>",id:"prerequisites-for-this-guide",level:2},{value:"<strong>First Steps</strong>",id:"first-steps",level:2},{value:"<strong>Setting up TLS</strong>",id:"setting-up-tls",level:2},{value:"<strong>Public HTTP API</strong>",id:"public-http-api",level:2},{value:"<strong>Generate drand Keys</strong>",id:"generate-drand-keys",level:2},{value:"<strong>Docker-compose Setup</strong>",id:"docker-compose-setup",level:2},{value:"<strong>Distributed Key Generation (DKG)</strong>",id:"distributed-key-generation-dkg",level:2},{value:"<strong>Other Topics</strong>",id:"other-topics",level:2},{value:"<strong>Updating drand</strong>",id:"updating-drand",level:3},{value:"<strong>Reset the Docker State (without losing the Keys)</strong>",id:"reset-the-docker-state-without-losing-the-keys",level:3},{value:"<strong>Method 1: using <code>drand util reset</code></strong>",id:"method-1-using-drand-util-reset",level:3},{value:"<strong>Method 2: performing a Manual Reset</strong>",id:"method-2-performing-a-manual-reset",level:3},{value:"<strong>Docker behind Reverse Proxy Setup</strong>",id:"docker-behind-reverse-proxy-setup",level:3}];function l(e){const n={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",header:"header",hr:"hr",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.header,{children:(0,s.jsx)(n.h1,{id:"docker-install",children:"Docker Install"})}),"\n",(0,s.jsx)(n.h1,{id:"install-drand-with-docker",children:(0,s.jsx)(n.strong,{children:"Install drand with Docker"})}),"\n",(0,s.jsxs)(n.p,{children:["This page describes how to run a ",(0,s.jsx)(n.em,{children:"production"})," version of ",(0,s.jsx)(n.code,{children:"drand"})," based on ",(0,s.jsx)(n.code,{children:"docker-compose"}),". This is meant as a ",(0,s.jsx)(n.strong,{children:"production setup"}),". It notably involves generating TLS certificates for your public-facing server. Check out the ",(0,s.jsx)(n.a,{href:"/operator/drand-cli/",children:(0,s.jsx)(n.strong,{children:"simple install if you just want a local drand demo \u2192"})})]}),"\n",(0,s.jsx)(n.h2,{id:"prerequisites-for-this-guide",children:(0,s.jsx)(n.strong,{children:"Prerequisites for this Guide"})}),"\n",(0,s.jsx)(n.p,{children:"a VPS with the following software setup:"}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"docker >= 17.12"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"docker-compose >= 1.18"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"go >= 1.22"})}),"\n",(0,s.jsx)(n.li,{children:'a TLS-capable reverse-proxy talking gRPC, or any other way to get TLS certificates (see section "Setting up TLS")'}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"first-steps",children:(0,s.jsx)(n.strong,{children:"First Steps"})}),"\n",(0,s.jsxs)(n.p,{children:["Create a new folder and copy the docker-compose file from ",(0,s.jsx)(n.code,{children:"drand/docker/docker-compose.yml"})," into it. Make sure to replace the image from ",(0,s.jsx)(n.code,{children:"ghcr.io/drand/go-drand-local:latest"})," to ",(0,s.jsx)(n.code,{children:"ghcr.io/drand/go-drand:latest"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["You may place this directory where you want, e.g. ",(0,s.jsx)(n.code,{children:"~/deploy-example"}),". Its name is irrelevant too, should you want to change it (just don't call it ",(0,s.jsx)(n.code,{children:"~/.drand"})," which is used for the config files)."]}),"\n",(0,s.jsx)(n.p,{children:"At this point, your current working directory should look like this:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"$ pwd\n/home/drand/deploy-example\n------------------------------------------------------------\n$ tree\
1n.\n\u2514\u2500\u2500 docker-compose.yml\n"})}),"\n",(0,s.jsx)(n.p,{children:"Next, create a named volume where you're going to store your keypairs and other config data:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"docker volume create drand\n"})}),"\n",(0,s.jsx)(n.h2,{id:"setting-up-tls",children:(0,s.jsx)(n.strong,{children:"Setting up TLS"})}),"\n",(0,s.jsx)(n.p,{children:"To be secure, drand needs authenticated channels to talk to other drand nodes. This can be currently done in two ways:"}),"\n",(0,s.jsxs)(n.p,{children:["Outgoing gRPC communication will be done assuming a TLS server, this can be disabled using the build tag ",(0,s.jsx)(n.code,{children:"-tags=conn_insecure"})," when building drand, or the ",(0,s.jsx)(n.code,{children:"go-drand-local"})," docker image. But incoming connections are expected to have been through a reverse-proxy in front of drand doing TLS termination for it."]}),"\n",(0,s.jsxs)(n.p,{children:["Note: If you're just trying to run an insecure demo, run ",(0,s.jsx)(n.code,{children:"make demo"})," in the root folder of this repository instead of following this guide."]}),"\n",(0,s.jsx)(n.p,{children:"This guide will continue focusing on drand; jump to the end of this guide to configure the reverse proxy."}),"\n",(0,s.jsx)(n.h2,{id:"public-http-api",children:(0,s.jsx)(n.strong,{children:"Public HTTP API"})}),"\n",(0,s.jsxs)(n.p,{children:["The compose file also opens a public http API to be consumed by the clients. This public endpoint is exposed on the 8081 port (specified with\xa0",(0,s.jsx)(n.code,{children:"--public-listen"}),"]). If you wish to not expose the public http endpoint, you need to change the docker file to remove references to public port."]}),"\n",(0,s.jsx)(n.h2,{id:"generate-drand-keys",children:(0,s.jsx)(n.strong,{children:"Generate drand Keys"})}),"\n",(0,s.jsx)(n.p,{children:"Now, let's generate keys for drand:"}),"\n",(0,s.jsx)(n.p,{children:"If you are using docker compose:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"docker compose exec <your_drand_service_name> drand generate-keypair <address:public-port>\n"})}),"\n",(0,s.jsxs)(n.p,{children:["This generates keys in ",(0,s.jsx)(n.code,{children:"~/.drand/keys/"}),", you can specify another folder using the ",(0,s.jsx)(n.code,{children:"--folder"})," flag."]}),"\n",(0,s.jsx)(n.p,{children:"If you're not using compose:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"docker run -v drand:/data/drand --rm ghcr.io/drand/go-drand-local 'generate-keypair <address:public-port>'\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Where ",(0,s.jsx)(n.code,{children:"<address>"})," is for instance ",(0,s.jsx)(n.code,{children:"drand.yourserver.com:8080"})," or ",(0,s.jsx)(n.code,{children:"yourserver.com:8080"}),". If you'll be using a reverse-proxy, as you should in production, make sure you enter its public-facing port."]}),"\n",(0,s.jsx)(n.h2,{id:"docker-compose-setup",children:(0,s.jsx)(n.strong,{children:"Docker-compose Setup"})}),"\n",(0,s.jsx)(n.p,{children:"drand can now be started as follows:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-jsx",children:"docker compose up -d\n"})}),"\n",(0,s.jsx)(n.p,{children:"To check what is happening, access the docker-compose logs via"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-jsx",children:"docker compose logs\n"})}),"\n",(0,s.jsx)(n.h2,{id:"distributed-key-generation-dkg",children:(0,s.jsx)(n.strong,{children:"Distributed Key Generation (DKG)"})}),"\n",(0,s.jsx)(n.p,{children:"If you did the setup above, you have a container running the drand deamon, loaded with your keys. It still needs to run the DKG protocol to bootstrap drand."}),"\n",(0,s.jsx)(n.p,{children:"Fortunately, with our docker-compose volumes, it's now very easy to add things into the running container."}),"\n",(0,s.jsx)(n.p,{children:"To do so, let's open a CLI into your running docker."}),"\n",(0,s.jsx)(n.p,{children:"First find its id on the host:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-jsx",children:'$ docker ps\n697e4766f8b2 drand_drand "drand --verbose 2 s\u2026" 11 minutes ago Up 9 minutes\n'})}),"\n",(0,s.jsxs)(n.p,{children:["The id of the container is ",(0,s.jsx)(n.code,{children:"697e4766f8b2"}),". Enter it by running:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-jsx",children:"docker exec -it 697e4766f8b2 /bin/sh\n"})}
1),"\n",(0,s.jsx)(n.p,{children:"Then, you're inside the container; tell drand to run the DKG like so:"}),"\n",(0,s.jsx)(n.p,{children:"If it is an initial DKG:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"drand dkg join --id <beaconID>\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Otherwise, if it is a resharing where your node is joining, you'll need to get the previous groupfile from existing participants and put it inside your container (you can ",(0,s.jsx)(n.code,{children:"curl"})," or ",(0,s.jsx)(n.code,{children:"wget"})," it from inside the container most likely) and run:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"drand dkg join --id <beaconID> --group /path/to/previous/group.toml\n"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Notice the full path"})," ",(0,s.jsx)(n.code,{children:"/path/to/previous/group.toml"})," and not ",(0,s.jsx)(n.code,{children:"group.toml"})," nor ",(0,s.jsx)(n.code,{children:"./group.toml"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["At this point, once ",(0,s.jsx)(n.em,{children:"everybody"})," in the ",(0,s.jsx)(n.code,{children:"group.toml"})," has accepted/joined the DKG, the leader can execute the DKG and the randomness generation will start. Well done! Simply let it run, there's nothing else to do."]}),"\n",(0,s.jsxs)(n.p,{children:["In general, we refer you to the ",(0,s.jsx)(n.a,{href:"/operator/drand-cli/",children:"drand-cli guide"})," page for up-to-date information about the drand cli."]}),"\n",(0,s.jsx)(n.h2,{id:"other-topics",children:(0,s.jsx)(n.strong,{children:"Other Topics"})}),"\n",(0,s.jsx)(n.h3,{id:"updating-drand",children:(0,s.jsx)(n.strong,{children:"Updating drand"})}),"\n",(0,s.jsx)(n.p,{children:"To update drand, simply pull the latest container"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-jsx",children:"docker compose pull\n"})}),"\n",(0,s.jsx)(n.p,{children:"Then rebuild and restart it"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-jsx",children:"docker compose down && docker compose up -d\n"})}),"\n",(0,s.jsx)(n.h3,{id:"reset-the-docker-state-without-losing-the-keys",children:(0,s.jsx)(n.strong,{children:"Reset the Docker State (without losing the Keys)"})}),"\n",(0,s.jsx)(n.p,{children:"This part is if you need to reset drand's internal state without loosing the keys."}),"\n",(0,s.jsx)(n.h3,{id:"method-1-using-drand-util-reset",children:(0,s.jsxs)(n.strong,{children:["Method 1: using ",(0,s.jsx)(n.code,{children:"drand util reset"})]})}),"\n",(0,s.jsx)(n.p,{children:"First, try using this method. If that doesn't work, use the method below."}),"\n",(0,s.jsx)(n.p,{children:"Find drand's container id on the host, and enter it:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-jsx",children:'$ docker ps\n697e4766f8b2 drand_drand "drand --verbose 2 s\u2026" 11 minutes ago Up 9 minutes\n\n$ docker exec -it 697e4766f8b2 /bin/sh\n'})}),"\n",(0,s.jsx)(n.p,{children:"Then simply call:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-jsx",children:"drand util reset\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Exit the container with ",(0,s.jsx)(n.code,{children:"CTRL-C"}),". Then, on the host, I advise you to restart the container (to make sure the drand deamon has a clean restart and can reload its cleaned config):"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"docker compose down\ndocker compose up -d\n"})}),"\n",(0,s.jsx)(n.h3,{id:"method-2-performing-a-manual-reset",children:(0,s.jsx)(n.strong,{children:"Method 2: performing a Manual Reset"})}),"\n",(0,s.jsxs)(n.p,{children:["The method above relies on ",(0,s.jsx)(n.code,{children:"drand util reset"}),", which could theoretically fail. If you want a manual hard-reset, start by killing the container:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"docker compose down\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Delete what you want to reset in the volume: typically, you absolutely want to ",(0,s.jsx)(n.strong,{children:"keep"})," ",(0,s.jsx)(n.code,{children:"keys"}),", ",(0,s.jsx)(n.strong,{children:"especially"})," if you shared those keys to create a ",(0,s.jsx)(n.code,{children:"group.toml"})," with other people. For instance if the DKG failed, remove the ",(0,s.jsx)(n.code,{children:"dkg.db"})," file, and the directories ",(0,s.jsx)(n.code,{children:"db"})," and ",(0,s.jsx)(n.code,{children:"groups"}),". Notice that if you added the ",(0,s.jsx)(n.code,{children:"group.toml"})," as suggested, it should still be there (don't delete it unless you want to change the group)."]}
1),"\n",(0,s.jsx)(n.p,{children:"Then, rebuild the image from scratch:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"docker compose up -d\n"})}),"\n",(0,s.jsx)(n.p,{children:"Check that things are running with"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"docker compose logs -f\n"})}),"\n",(0,s.jsx)(n.p,{children:'You\'re now back to the step "Distributed Key Generation" of this guide.'}),"\n",(0,s.jsx)(n.h3,{id:"docker-behind-reverse-proxy-setup",children:(0,s.jsx)(n.strong,{children:"Docker behind Reverse Proxy Setup"})}),"\n",(0,s.jsx)(n.p,{children:"Typically, the TLS part of my VPS is managed by a single reverse proxy, which then proxies multiple services running locally with docker."}),"\n",(0,s.jsxs)(n.p,{children:["There is one subtletly: you need to forward ",(0,s.jsx)(n.em,{children:"both"}),' GRPC (used by drand "core") and web traffic (for the web interface). To forward GRPC, you need to have nginx ',(0,s.jsx)(n.code,{children:"1.13.10"})," or above, it's a fairly recent addition."]}),"\n",(0,s.jsxs)(n.p,{children:["Then, you need to forward differently traffic to private API port and the HTTP public API port. Here's an example configuration for ",(0,s.jsx)(n.code,{children:"nginx"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-nginx",children:"server {\n server_name drand.example.ch;\n listen 443 ssl http2;\n\n location / {\n // default --private-listen port specified in the docker compose\n grpc_pass grpc://localhost:1234; // Adjust port if needed\n grpc_set_header X-Real-IP $remote_addr;\n }\n\n ssl_certificate /etc/letsencrypt/live/.../fullchain.pem; # managed by Certbot\n ssl_certificate_key /etc/letsencrypt/live/.../privkey.pem; # managed by Certbot\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:["You can separate the entries in two in nginx by having two ",(0,s.jsx)(n.code,{children:"server"})," blocks to apply different configurations."]}),"\n",(0,s.jsx)(n.hr,{})]})}function h(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(l,{...e})}):l(e)}},28453(e,n,r){r.d(n,{R:()=>d,x:()=>i});var o=r(96540);const s={},t=o.createContext(s);function d(e){const n=o.useContext(t);return o.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function i(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:d(e.components),o.createElement(t.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.