# Installing on Kubernetes

**URL:** https://meta.discourse.org/t/installing-on-kubernetes/49329
**Category:** Self-hosting
**Tags:** unsupported-install
**Created:** [August 27, 2016, 10:59pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329 "2016-08-27T22:59:09Z")
**Posts on this page:** 20
**Page:** 2

<div class="post-metadata">

### Author: ![sam](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/sam/32/102149_2.png) [@sam](https://meta.discourse.org/u/sam)
#### Post date: [January 28, 2019, 9:03am UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/21 "2019-01-28T09:03:21Z")

</div>

> [@joshuaavalon](#):
>
> does this mean I have to run it again on the production database?

Yes

> [@joshuaavalon](#):
>
> Is `DISCOURSE_DB_HOST` and `DISCOURSE_REDIS_HOST` written to the image? If not, how can I change it on the image?

Whatever ENV you specify when you finally do you `docker run` on the image … will take.

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [January 28, 2019, 8:04pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/22 "2019-01-28T20:04:58Z")

</div>

> [@sam](#):
>
> > does this mean I have to run it again on the production database?
> 
> Yes

And doing those things at every boot of the container is reasonable?

---

<div class="post-metadata">

### Author: ![sam](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/sam/32/102149_2.png) [@sam](https://meta.discourse.org/u/sam)
#### Post date: [January 28, 2019, 8:08pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/23 "2019-01-28T20:08:02Z")

</div>

Some people like this pattern, I do not, that is why rails introduces some super fancy locking around migrations to ensure they never run concurrently when a cluster of 20 app try to run migrations at the same time.

Some people just boot the same image and run the migrations in some out-of-band process.

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [January 28, 2019, 8:15pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/24 "2019-01-28T20:15:18Z")

</div>

But each container should still `rake assets:precompile` at least once?

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [January 28, 2019, 8:49pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/25 "2019-01-28T20:49:39Z")

</div>

Depends on how do you host those.

If you have a NFS share of some sort (like AWS EFS) or if you upload those to some object storage service that can only be done on bootstrap.

---

<div class="post-metadata">

### Author: ![Geoffrey\_Challen](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/geoffrey_challen/32/119637_2.png) [@Geoffrey\_Challen](https://meta.discourse.org/u/Geoffrey_Challen)
#### Post date: [February 7, 2020, 2:55pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/26 "2020-02-07T14:55:05Z")

</div>

Thanks to everyone for all of the helpful replies. I have a few more questions about how to do this properly.

I’m trying to prepare a minimal app.yaml for bootstrapping that only contains information required for container build. Some of the information in there is clearly intended for container runtime—like the volume mounts and port mappings. But I’m not sure about the environment variables. I guess I’ll just try it, but are those environment variables used during container build (injected into the Dockerfile somehow) or just at container runtime? If it’s the latter I’ll just make sure they end up in the appropriate k8s configuration file.

Second, some people here have talked about pushing the image to a private container repository. Is that required? Put another way—does the build image contain any secret information that shouldn’t be published to a public repo like Docker Hub? (We don’t have a private container repository yet and I’d like to avoid setting one up.)

Finally, is there an app.yaml setting for controlling the name of the created container? More of just a fit and finish thing, but that would be nice 🙂.

Thanks in advance for the help! (Sorry for bumping an old thread. This is the first hit on Google when searching for how to install Discourse on Kubernetes.)

---

<div class="post-metadata">

### Author: ![lucasbasquerotto](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lucasbasquerotto/32/133376_2.png) [@lucasbasquerotto](https://meta.discourse.org/u/lucasbasquerotto)
#### Post date: [February 7, 2020, 6:37pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/27 "2020-02-07T18:37:28Z")

</div>

@Geoffrey_Challen You can create an image with the discourse repository and plugins, install the ruby gems and other dependencies and push it to a registry (like DockerHub). This repository would be **agnostic to the environment** and could be public (unless you include a private plugin or something of the sort). This base image could be used in staging and production environments and even in different projects (if they use the same plugins).

Steps like precompiling the assets, db migration and generating the ssl certificate should be executed in the target machine, though, to generate the final image.

I don’t know exactly how to include that in a k8s cluster tough. I went with the conservative approach and use it based on the official guide from the discourse team, just separating in 2 steps.

> [@Why is "rebuild" so tightly coupled to container run status?](https://meta.discourse.org/t/why-is-rebuild-so-tightly-coupled-to-container-run-status/136414/4):
>
> About updating discourse source, os level dependencies, the docker base image, ruby gems and the like, it’s possible to do that making the build in 2 steps, and run the aforementioned tasks in the 1st step. This first step is agnostic to the environment and could be run even in a CI environment (so you could use an almost identical image in staging and production environments, avoiding possible errors due to rebuilding in different dates, not to mention the reduced downtime). The db migration …

---

<div class="post-metadata">

### Author: ![Geoffrey\_Challen](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/geoffrey_challen/32/119637_2.png) [@Geoffrey\_Challen](https://meta.discourse.org/u/Geoffrey_Challen)
#### Post date: [February 7, 2020, 7:48pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/28 "2020-02-07T19:48:44Z")

</div>

> [@lucasbasquerotto](#):
>
> Steps like precompiling the assets, db migration and generating the ssl certificate should be executed in the target machine, though, to generate the final image.

This part I’m not sure I understand. Won’t these happen automatically _inside_ the container as needed? I’m hoping I can just push this into our cloud and never need to gain shell access to the machine—similar to how I rarely (if ever) need to enter the Discourse Docker container.

---

<div class="post-metadata">

### Author: ![lucasbasquerotto](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lucasbasquerotto/32/133376_2.png) [@lucasbasquerotto](https://meta.discourse.org/u/lucasbasquerotto)
#### Post date: [February 7, 2020, 9:06pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/29 "2020-02-07T21:06:06Z")

</div>

You don’t need to enter the container explicitly. What I mean is that you can’t generate a precompiled image with them (generated in a CI pipeline, for example) and use it as is, because this must be executed in the target machine, where the database is located (this could be automated, but I haven’t done that in k8s, although I did it with Ansible).

---

<div class="post-metadata">

### Author: ![Geoffrey\_Challen](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/geoffrey_challen/32/119637_2.png) [@Geoffrey\_Challen](https://meta.discourse.org/u/Geoffrey_Challen)
#### Post date: [February 7, 2020, 9:59pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/30 "2020-02-07T21:59:33Z")

</div>

> [@lucasbasquerotto](#):
>
> because this must be executed in the target machine, where the database

Ah, OK. I’m using the all-in-one templates to include the database in the container. This is appropriate for our use case which involves supporting classes consisting of 10~1000 students—or at least the all-in-one has worked fine for my class in this configuration. So the database is inside the container.

But regardless, won’t Discourse run the database migrations or other setup steps when the container boots?

---

<div class="post-metadata">

### Author: ![lucasbasquerotto](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lucasbasquerotto/32/133376_2.png) [@lucasbasquerotto](https://meta.discourse.org/u/lucasbasquerotto)
#### Post date: [February 7, 2020, 11:10pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/31 "2020-02-07T23:10:24Z")

</div>

> [@Geoffrey\_Challen](#):
>
> I’m using the all-in-one templates to include the database in the container.

Are you sure the database is inside the container? Or is it the RDBMS (in this case, PostgreSQL)? The supported install uses the database outside the container (which is expected), mapping a volume inside to the outside (the host). Furthermore after a container rebuild, the container is recreated and you would lose all data.

If it really is inside the container I don’t know exactly how you would be able to upgrade based on the [official install](https://meta.discourse.org/t/142537?silent=true), because the [launcher](https://github.com/discourse/discourse_docker/blob/master/launcher) script seems to create and destroy the container several times when rebuilding (and run with `--rm`, which means that you would lose all your data, including the database, after the container stops).

I haven’t tried to change the way the rebuild is done, but assuming that you are able change it to run everything inside the container, without recreating it, then you should be able to push the container to a registry (make sure it’s private, because the secrets would be there). That said, I don’t recommend this approach for several reasons (some of them mentioned before).

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [February 7, 2020, 11:14pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/32 "2020-02-07T23:14:34Z")

</div>

The [standard install](https://meta.discourse.org/t/142537?silent=true) includes nginx, rails, postgres, and redis inside the container. It uses external volumes for the postgres and redis data. It is not destroyed on a rebuild/upgrade.

---

<div class="post-metadata">

### Author: ![lucasbasquerotto](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lucasbasquerotto/32/133376_2.png) [@lucasbasquerotto](https://meta.discourse.org/u/lucasbasquerotto)
#### Post date: [February 8, 2020, 11:52am UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/33 "2020-02-08T11:52:43Z")

</div>

> [@pfaffman](#):
>
> It is not destroyed on a rebuild/upgrade.

Yeah, just found weird that he said that the database is inside the container, unless he changed the way the [standard install](https://meta.discourse.org/t/142537?silent=true) works, or mean postgres, not the database itself.

---

<div class="post-metadata">

### Author: ![riking](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/riking/32/170938_2.png) [@riking](https://meta.discourse.org/u/riking)
#### Post date: [February 9, 2020, 8:12am UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/34 "2020-02-09T08:12:44Z")

</div>

> [@Geoffrey\_Challen](#):
>
> But regardless, won’t Discourse run the database migrations or other setup steps when the container boots?

No - the migrations & asset compilation steps happen during the `./launcher bootstrap` phase, after plugins have been resolved. After that’s done, the container can be restarted as much as necessary, or the web processes split across multiple machines, etc.

* * *

Imagineering, the setup should look something like this:

- official builds provide the discourse base image already.
  - we will also need a container for discourse\_docker that comes with its own Docker

- establish private registry inside the cluster
- establish a ConfigMap with the app.yml contents
- run a Job that executes `./launcher bootstrap` using the nested Docker on a VM-based Node (no docker socket access) and renames & pushes the resulting image to the private registry (with a timestamp-based label, not `latest`) (`local_discourse` is not a good name here) and rolls the deployment to the new label
  - wow, that’s a lot of permissions on the upgrade job.

---

<div class="post-metadata">

### Author: ![Geoffrey\_Challen](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/geoffrey_challen/32/119637_2.png) [@Geoffrey\_Challen](https://meta.discourse.org/u/Geoffrey_Challen)
#### Post date: [February 9, 2020, 2:49pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/35 "2020-02-09T14:49:48Z")

</div>

> [@lucasbasquerotto](#):
>
> Yeah, just found weird that he said that the database is inside the container,

postgres runs inside the container. It saves data outside the container, but it runs inside the container if you use the standard set of install templates. Ditto with redis. I think the confusion is when I say “the database runs inside container” I’m talking about the database server, even if the database files live outside the container. (But database files don’t “run”, which is why I consider my phrasing clear—but clearly not clear enough 🙂.)

PS: actually it doesn’t necessarily even save data outside the container unless you configure Docker to bind mount that directory. I’ve been able to skip this during bootstrap, although it’s probably not a good idea, since at that point the database contents won’t survive container restarts.

---

<div class="post-metadata">

### Author: ![Geoffrey\_Challen](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/geoffrey_challen/32/119637_2.png) [@Geoffrey\_Challen](https://meta.discourse.org/u/Geoffrey_Challen)
#### Post date: [February 9, 2020, 3:45pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/36 "2020-02-09T15:45:18Z")

</div>

I think that this is making more sense to me now, particularly after reading the long linked conversation regarding docker-compose, the launcher script, etc.

Here’s what I’d like to be able to do:

- Run `./launcher bootstrap` locally to create a “fat” Discourse image that includes all dependencies: postgres, redis, etc.
- Deploy that image on Kubernetes
- Sometime later rerun `./launcher bootstrap` to update the image and redeploy without destroying data (duh)

My understanding is that the fat Discourse image should not require any external service dependencies. However, for data to survive container upgrades the postgres database files need to live outside the container. That’s fine—I can create a k8s persistent volume for them.

Now here’s the only problem that I anticipate. Most of what happens during `./launcher bootstrap` only touches files that live inside the container. For example, precompiling assets. That’s fine, since the results live inside the container and don’t need to survive upgrades.

**The big exception here is the database migration.** That step needs to have access to the database that will be used _after_ bootstrapping is complete. So, to me, this seems like the major barrier to easily deploying fat Discourse images to the cloud.

I’ve noticed that @sam has mentioned multiple times that they redeploy Discourse for their customers using a workflow roughly similar to what I described above. But I suspect that the reason that this works is that their Discourse images are configured to use a database server (and probably Redis as well) that run on their cluster—which would make sense for supporting multiple deployments but is not quite what I want to do. This means that the bootstrapping process can modify the production database—or maybe just that the database migration step gets skipped entirely since database upgrades and migrations are handled externally. @sam: could you confirm?

Anyway, the upshot of this for me is that I need to find some way to run the database migrations when the container starts, not during `./launcher bootstrap`. I guess at that point one way to do this would be:

- Build the fat Discourse container locally using `./launcher bootstrap`, using a volume mount pointing at an empty local database, since that database is not going to be used later. This would get everything in the container right, just not finish the postgres work.
- Find a way to run the database migration step on the actual production database—perhaps using an k8s init container?
- Replace the old Discourse image with the new one

---

<div class="post-metadata">

### Author: ![riking](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/riking/32/170938_2.png) [@riking](https://meta.discourse.org/u/riking)
#### Post date: [February 9, 2020, 9:38pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/37 "2020-02-09T21:38:40Z")

</div>

You may be interested in a multi site configuration.

> [@Multisite configuration with Docker](https://meta.discourse.org/t/multisite-configuration-with-docker/14084):
>
> warning While multisite is supported in the Discourse application, this is an advanced sysadmin setup. If you don’t know what you’re doing, do not set up multisite. The Discourse team is unable to provide multisite configuration support. If you wish to host multiple domains on a singled Docker setup, you’ll need a multisite configuration. Here are the basic building blocks for one. Understand hooks Multisite is a fairly advanced topic. Before attempting a multisite build, spend some time…

There’s two big problems you’re running into: Discourse isn’t ready for Kubernetes, so custom code is required. And you’re edging into what the Discourse team does to make money (hosting a large number of forums), so the level of support you’re getting will drop off.

My advice? Do a multisite configuration with static scheduling onto VMs, entirely outside your cluster. (Or a Service Type=ExternalName pointing to the VM to keep the same Ingress.)

---

<div class="post-metadata">

### Author: ![Geoffrey\_Challen](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/geoffrey_challen/32/119637_2.png) [@Geoffrey\_Challen](https://meta.discourse.org/u/Geoffrey_Challen)
#### Post date: [February 9, 2020, 11:24pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/38 "2020-02-09T23:24:23Z")

</div>

OK… I managed to figure out one way of doing this. I’m not 100% happy with it, but it does work and may be appealing to others that are trying a simple single-container fat (includes postgres, redis, etc.) Discourse image deployment to Kubernetes.

### My Approach

After examining the bootstrap process it became clear to me that unfortunately it mixes in two different kinds of operations—ones that only affect the underlying container, and others that poke out into the surrounding environment, mainly through the `/shared` volume mount where the postgres data files live. Rather than trying to tease these steps apart, it seems more sane to just run the bootstrapping steps in the environment where the container is actually going to be deployed.

Unfortunately, `launcher bootstrap` wants to create a container and use Docker. So running `launcher` inside another container (for example, in a container running on our cloud) means either tangling with a Docker-in-Docker setup (doable, but not considered best practices) or exposing the underlying Docker daemon. I’m not even sure that that second approach would work, since I think that it would interpret a volume mount against the node’s local filesystem, whereas in our scenario we want to volume mount `/shared` to a persistent Kubernetes volume. _Maybe_ the Docker-in-Docker route would work, but then you’d also have a weird triple volume mount from inside the nested container into the outer container and from there to the persistent Kubernetes volume. That sounds… unwise.

However, essentially `launcher bootstrap` creates one large `.yml` file by processing the `templates` value in the `app.yml` and then passes that to the Discourse base image when finishes the bootstrap process. So if we can extract the configuration file we can generate the configuration on any machine and then we only need to figure out how to pass it to a container we start in the cloud.

So as an overview, here are the steps we are going to follow:

1. Generate the bootstrap configuration using a modified `launcher`
2. Pass that to a modified Discourse base image that will perform the bootstrapping (using `pups`) and then start Discourse

### Generating the Bootstrapping Configuration

Here’s the required change to `launcher` to support a `dump` command that writes the merged configuration to STDOUT:

```bash
run_dump() {
  set_template_info
  echo "$input"
}

```

(Note that this command is available in [our fork of `discourse_docker`](https://github.com/cs125-illinois/discourse_docker).)

So the first step is to use the new `launcher dump` command added above to create our bootstrap configuration:

```bash
# Substitute whatever your container configuration is called for app
./launcher dump app > bootstrap.yml

```

### Initial Container Creation

Next we need a container that knows to run `pups` to bootstrap the container before booting via `/sbin/boot`. I used the following Dockerfile to make a tiny change to the base discourse image:

```Dockerfile
FROM discourse/base:2.0.20191219-2109
COPY scripts/bootstrap.sh /
CMD bash bootstrap.sh

```

Where `scripts/bootstrap.sh` contains:

```bash
cd /pups/ && /pups/bin/pups --stdin < /bootstrap/bootstrap.yml && /sbin/boot

```

I published this as `geoffreychallen:discourse_base:2.0.20191219-2109`. (Note that you could probably also accomplish the same thing by modifying the boot command of the base Discourse docker image, but I was having a hard time getting that to work with the shell redirection required to get `pups` to read the configuration file.)

### Kubernetes Configuration

Now we need our Kubernetes configuration. Mine looks like this:

```yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: kotlin-forum-pvc
  namespace: ikp
spec:
  storageClassName: rook-ceph-block
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 64Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kotlin-forum-deployment
  namespace: ikp
spec:
  replicas: 0
  selector:
    matchLabels:
      app: kotlin-forum
  template:
    metadata:
      labels:
        app: kotlin-forum
    spec:
      volumes:
      - name: kotlin-forum
        persistentVolumeClaim:
          claimName: kotlin-forum-pvc
      - name: bootstrap
        configMap:
          name: kotlin-forum-bootstrap
      containers:
      - name: kotlin-forum
        image: geoffreychallen/discourse_base:2.0.20191219-2109
        imagePullPolicy: Always
        volumeMounts:
        - name: kotlin-forum
          mountPath: /shared/
        - name: bootstrap
          mountPath: /bootstrap/
        ports:
        - containerPort: 80
        env:
        - name: TZ
          value: "America/Chicago"
        - name: LANG
          value: en_US.UTF-8
        - name: RAILS_ENV
          value: production
        - name: UNICORN_WORKERS
          value: "3"
        - name: UNICORN_SIDEKIQS
          value: "1"
        - name: RUBY_GLOBAL_METHOD_CACHE_SIZE
          value: "131072"
        - name: RUBY_GC_HEAP_GROWTH_MAX_SLOTS
          value: "40000"
        - name: RUBY_GC_HEAP_INIT_SLOTS
          value: "400000"
        - name: RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR
          value: "1.5"
        - name: DISCOURSE_DB_SOCKET
          value: /var/run/postgresql
        - name: DISCOURSE_DEFAULT_LOCALE
          value: en
        - name: DISCOURSE_HOSTNAME
          value: kotlin-forum.cs.illinois.edu
        - name: DISCOURSE_DEVELOPER_EMAILS
          value: challen@illinois.edu
        - name: DISCOURSE_SMTP_ADDRESS
          value: outbound-relays.techservices.illinois.edu
        - name: DISCOURSE_SMTP_PORT
          value: "25"
---
apiVersion: v1
kind: Service
metadata:
  name: kotlin-forum
  namespace: ikp
spec:
  type: NodePort
  ports:
  - name: http
    port: 80
    targetPort: 80
  selector:
    app: kotlin-forum
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  namespace: ikp
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  name: kotlin-forum-ingress
spec:
  rules:
  - host: kotlin-forum.cs.illinois.edu
    http:
      paths:
      - backend:
          serviceName: kotlin-forum
          servicePort: 80

```

Yours will look different. Note that I’m terminating HTTPS upstream, hence the modifications to the Ingress configuration. I also like to put everything in one file, delete pieces that don’t work as I iterate, and then let Kubernetes skip duplicates on the next `kubectl create -f`. Also note that I set `replicas: 0` so that the deployment doesn’t start as soon as its configured. That’s because we have one bit of additional configuration to finish.

I copied the list of environment variables from what I saw being passed to the container by `launcher start`. I don’t know if all of these are necessary and others may be missing depending on your configuration. YMMV.

Note that we have two volume maps pointing into the container: the first is for postgres, configured as a persistent volume that will survive pod restarts. The second is a configuration mapping created like this:

```bash
kubectl create configmap kotlin-forum-bootstrap --from-file=bootstrap.yml=<path/to/bootstrap.yml>

```

Where `kotlin-forum-bootstrap` needs to match your Kubernetes configuration and `path/to/bootstrap.yml` is the path to the bootstrap.yml file we created using `launcher dump` above.

Once your `configmap` is in place, you should be able to scale your deployment to one replica and see Discourse booting and running the same bootstrap process that `launcher bootstrap` would have performed. That takes a few minutes. When that is done, your Discourse installation will boot.

### Others Bits of Configuration

A few other notes that I ran on the way to getting this (at least for now) fully configured:

- Any upstream proxies _must_ forward `X-Forwarded` headers, including both `X-Fowarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Port`. Not doing so will result in strange authentication errors when trying to use Google login and probably other login providers.
- Your `nginx` ingress controller must be configured to pass headers by setting `use-forwarded-headers` in the global config map. This took me a while to get right, since at least several times I edited the wrong configuration map, and then expected my ingress containers to restart when the configuration map changed. (They didn’t.)

### Updating

To update the deployed installation, you regenerate the new `bootstrap.yml` file, update the config map, and then restart the container (easiest by scaling to 0 and then back to 1 replica).

This does incur a bit of downtime since the bootstrapping happens before the container is built. But this seems inevitable to me in cases where you need to update the configuration and/or change the base image. `launcher rebuild` is documented as `stop; bootstrap; start`, meaning that the bootstrap process will still cause downtime even if performed using the launcher script.

### Comments

This fat container Discourse deployment pattern would be much easier to support if the `launcher` script would more cleanly separate (a) bootstrap steps that could be performed offline and only affect the files in the container and (b) bootstrap steps that modify or need access to the database or other state outside the container. The approach described above is a bit frustrating because you do see all kinds of JS uglification, asset minification, and other things that could be done with the previous deployment running… but they are just too mixed in with other things (like database migrations) that can’t be done without access to the database. I briefly thought about creating a container that would only perform the steps in `templates/postgres.yml`, but then noticed that database migrations were being done by the web template, and thought about plugins, and then just gave up 🙂.

With better separation redeployment for fat containers could work something like this:

- Build new fat container offline performing all steps internal to the container
- Publish that container
- When ready to upgrade, stop the previous container, start the new fat container, and let it finish any steps that need database access. Based on my experimentation those seem faster than some of the other bootstrap steps.

That would result in a bit less downtime. It’s probably not worth the effort for that reason alone, but I can imagine that this might also simplify more complex deployment scenarios involving shared databases or whatever.

---

<div class="post-metadata">

### Author: ![lucasbasquerotto](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lucasbasquerotto/32/133376_2.png) [@lucasbasquerotto](https://meta.discourse.org/u/lucasbasquerotto)
#### Post date: [February 10, 2020, 12:35pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/39 "2020-02-10T12:35:08Z")

</div>

> [@Geoffrey\_Challen](#):
>
> I’m talking about the database server, even if the database files live outside the container.

That makes more sense. That’s what I did when separating in 2 steps the bootstrap stage. The 1st can run in an isolated environment (like a CI pipeline) generating a base image with the discourse repository, gems and plugins installed, and the 2nd step needs to run in the target machine (or at least have access to the production database) to do the db migration and generating the assets (this is done in the bootstrap process tough, not when starting the container).

> [@Geoffrey\_Challen](#):
>
> This fat container Discourse deployment pattern would be much easier to support if the `launcher` script would more cleanly separate (a) bootstrap steps that could be performed offline and only affect the files in the container and (b) bootstrap steps that modify or need access to the database or other state outside the container.

Yes that would be awesome. [I requested that already](https://meta.discourse.org/t/bootstrap-app-container-in-2-steps/116865), but I don’t know if and when that will be done.

> [@Geoffrey\_Challen](#):
>
> The approach described above is a bit frustrating because you do see all kinds of JS uglification, asset minification, and other things that could be done with the previous deployment running

That would be difficult to implement completely on a separate environment because the assets precompile task needs access to the database (for things like custom css), but would be great if only what depends on the database could be made in a separate step (and all other assets, that don’t depend on the database, could be precompiled separately, but I don’t know how viable would be to implement it, technically).

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [February 10, 2020, 3:39pm UTC](https://meta.discourse.org/t/installing-on-kubernetes/49329/40 "2020-02-10T15:39:43Z")

</div>

That’s pretty much what I do on the kubernetes installs that I’ve done. I can’t imagine how or why to use k8s without separate data and web containers (or some other kind of external postgres and redis–the installs I’ve done for clients use GCP resources for that).

Also, there is an environment variable skip\_post\_migration\_updates that you need to understand for true zero downtime upgrades. It’s described [here](https://meta.discourse.org/t/zero-downtime-upgrades/120673/2).

[Previous page](https://meta.discourse.org/t/installing-on-kubernetes/49329.md?page=1)

[Next page](https://meta.discourse.org/t/installing-on-kubernetes/49329.md?page=3)
