import{r,p as c,q as u,w as t,z as e,t as l,R as o}from"./vendor.3d5b4206.js";const d={class:"markdown-body"},h=e("p",null,[o("While there are many different ways to run Directus on GCP, from single "),e("a",{href:"https://cloud.google.com/compute",target:"_blank",rel:"noopener noreferrer"},"Compute Engine"),o(" instances to a full "),e("a",{href:"https://cloud.google.com/kubernetes-engine",target:"_blank",rel:"noopener noreferrer"},"Kubernetes Enginge"),o(" stack, we like the following combination of services:")],-1),p=e("ul",null,[e("li",null,[e("a",{href:"https://cloud.google.com/run",target:"_blank",rel:"noopener noreferrer"},"Google Cloud Run")]),e("li",null,[e("a",{href:"https://cloud.google.com/sql",target:"_blank",rel:"noopener noreferrer"},"Google Cloud SQL")]),e("li",null,[e("a",{href:"https://cloud.google.com/storage",target:"_blank",rel:"noopener noreferrer"},"Google Cloud Storage")])],-1),g=e("h2",{id:"google-cloud-run"},[o("Google Cloud Run "),e("a",{class:"header-anchor",href:"#google-cloud-run"},"#")],-1),_=e("p",null,"We will run Directus in an autoscaling managed container environment called Google Cloud Run. Google Cloud Run will scale back to zero instances by default but can be configured to meet every scalability requirements.",-1),m=e("p",null,[o("While there a numerous ways to get a Docker container up and running in Cloud Run, we will talk you through a simple setup with still a lot of improvements to be made. The section "),e("a",{href:"#additional-improvements"},"Additional improvements"),o(" stipulates additional improvements you can make to your setup.")],-1),f=e("h2",{id:"google-cloud-sql"},[o("Google Cloud SQL "),e("a",{class:"header-anchor",href:"#google-cloud-sql"},"#")],-1),y=e("p",null,"Fully managed relational database service for MySQL, PostgreSQL, and SQL Server. This will be the persistent database layer. In this guide we will use PostgreSQL.",-1),b=e("h2",{id:"google-cloud-storage"},[o("Google Cloud Storage "),e("a",{class:"header-anchor",href:"#google-cloud-storage"},"#")],-1),w=e("p",null,"Will be used as object/file storage. Since we\u2019re running Directus in Google Cloud, it\u2019ll be authenticated and authorized to access all storage buckets in the same Google Cloud project by default. We just need to set it up in the environment variables.",-1),k=e("h2",{id:"cost"},[o("Cost "),e("a",{class:"header-anchor",href:"#cost"},"#")],-1),C=e("p",null,[o("Running Directus in Google Cloud can and will infer costs. Please read and understand all pricing options per service before rolling out Directus on Google Cloud. As a rule of thumb you could say that with the setup written here the main costs come from a persistent Postgres in CloudSQL. You can estimate the pricing here: "),e("a",{href:"https://cloud.google.com/products/calculator",target:"_blank",rel:"noopener noreferrer"},"https://cloud.google.com/products/calculator")],-1),S=e("h2",{id:"cloud-run-click-to-deploy"},[o("Cloud Run click to deploy "),e("a",{class:"header-anchor",href:"#cloud-run-click-to-deploy"},"#")],-1),v=e("p",null,"Just to see Directus work on Google Cloud you can try it out by clicking this button. Keep in mind, this will use a non persistent SQLite database inside the container itself, so everything will be lost once the container shuts down. And it will shut down.",-1),D=e("p",null,[e("a",{href:"https://deploy.cloud.run/?git_repo=https://github.com/directus-community/gcp-example",target:"_blank",rel:"noopener noreferrer"},[e("img",{src:"https://deploy.cloud.run/button.svg",alt:"Run on Google Cloud"})])],-1),R=e("p",null,[o("After deploying you can login with "),e("code",null,"admin@example.com"),o(" and "),e("code",null,"localpassword")],-1),G=e("h2",{id:"walkthrough-complete-setup"},[o("Walkthrough complete setup "),e("a",{class:"header-anchor",href:"#walkthrough-complete-setup"},"#")],-1),A=e("p",null,"Let\u2019s get into it.",-1),j=e("li",null,[e("p",null,[o("Install the "),e("a",{href:"https://cloud.google.com/sdk/docs/install",target:"_blank",rel:"noopener noreferrer"},"Google Cloud SDK"),o(". We will run a lot of "),e("code",null,"gcloud"),o(" commands to get your GCP environment setup.")])],-1),E=o("Go over the steps for "),T=o("Manually installing Directus"),I=e("li",null,[e("p",null,"Add a start script to your package.json like so:")],-1),L=e("pre",null,[e("code",null,`"scripts": {
		"start": "npx directus bootstrap; npx directus start"
}
`)],-1),q=e("ol",{start:"4"},[e("li",null,[o("Add a new "),e("code",null,"Dockerfile"),o(" file to the root of your newly setup Directus folder and add these contents")])],-1),O=e("pre",null,[e("code",null,`FROM node:16-alpine

WORKDIR /src

ADD . /src

RUN npm install --production

CMD ["npm", "run", "start"]
`)],-1),P=e("ol",{start:"5"},[e("li",null,[e("p",null,[o("Create a new project in "),e("a",{href:"https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project",target:"_blank",rel:"noopener noreferrer"},"Google Cloud"),o(": "),e("code",null,"gcloud projects create <your-project-id>"),o(" and make sure "),e("code",null,"your-project-id"),o(" is globally unique. Write down this ID as it will be used in all subsequent gcloud commands.")])]),e("li",null,[e("p",null,[o("Add/link a billing account/details to your project by going here: "),e("code",null,"https://console.cloud.google.com/billing/linkedaccount?project=<your-project-id>"),o(". Since the database and storage bucket are persistent, there are (costs)[#cost] involved.")])]),e("li",null,[e("p",null,"Create a storage bucket where Directus will store files:")])],-1),N=e("pre",null,[e("code",null,`gsutil mb -p <your-project-id> -c standard -l europe-west4 gs://<unique-bucket-name>/
`)],-1),x=e("p",null,"Some notes:",-1),B=e("ul",null,[e("li",null,"The region (europe-west4) is the region where the bucket resides. It\u2019s a good idea, sometimes even mandatory, to set all services to the same region."),e("li",null,"The bucket name should be globally unique")],-1),W=e("ol",{start:"8"},[e("li",null,"Create a Postgres13 database:")],-1),M=e("pre",null,[e("code",null,`gcloud sql instances create <database-instance-name> --region=europe-west4 --tier=db-f1-micro --project=<your-project-id> --database-version=POSTGRES_13
`)],-1),Q=e("p",null,"Some notes:",-1),U=e("ul",null,[e("li",null,"The region (europe-west4) is the region where the database resides. It\u2019s a good idea, sometimes even mandatory, to set all services to the same region."),e("li",null,[o("The "),e("a",{href:"https://cloud.google.com/sql/docs/postgres/instance-settings",target:"_blank",rel:"noopener noreferrer"},"tier"),o(" determines resources and cost of the database. For this example we\u2019ve picked the smallest one.")]),e("li",null,[o("This operation will take a while. If, for some reason, the gcloud times out, you can still find your database instance and its status "),e("a",{href:"https://console.cloud.google.com/sql/instances",target:"_blank",rel:"noopener noreferrer"},"here"),o(".")])],-1),V=e("p",null,"The output will look something like this:",-1),Y=e("pre",null,[e("code",null,`Creating Cloud SQL instance...done.
Created [https://sqladmin.googleapis.com/sql/v1beta4/projects/your-project-id/instances/your-project-id-pg13].
NAME                         DATABASE_VERSION  LOCATION        TIER         PRIMARY_ADDRESS  PRIVATE_ADDRESS  STATUS
your-project-id-pg13         POSTGRES_13       europe-west4-b  db-f1-micro  123.456.789.0    -                RUNNABLE
`)],-1),K=e("p",null,[o("Write down the IP address (in this example "),e("code",null,"123.456.789.0"),o("), you\u2019ll need to set it in your "),e("code",null,".env"),o(" later")],-1),z=e("ol",{start:"9"},[e("li",null,"Set the root user password in your database:")],-1),F=e("pre",null,[e("code",null,`gcloud sql users set-password root --host=% --instance <database-instance-name> --password <your-safe-root-password> --project=<your-project-id>
`)],-1),H=e("ol",{start:"10"},[e("li",null,"Create the directus database:")],-1),J=e("pre",null,[e("code",null,`gcloud sql databases create directus --instance=<database-instance-name> --project=<your-project-id>
`)],-1),X=e("p",null,[o("In this example the database is called "),e("code",null,"directus")],-1),Z=e("ol",{start:"11"},[e("li",null,"Get the connection name of your CloudSQL instance:")],-1),$=e("pre",null,[e("code",null,`gcloud sql instances describe <database-instance-name> --project=<your-project-id>
`)],-1),ee=e("p",null,[o("You will need the value of "),e("code",null,"connectionName"),o(" in step 12 and 14.")],-1),oe=e("ol",{start:"12"},[e("li",null,[o("Add these items to your "),e("code",null,".env"),o(" file:")])],-1),te=e("pre",null,[e("code",null,`DB_CLIENT="pg"
DB_PORT="5432"
DB_DATABASE=directus
DB_USER=root
DB_PASSWORD=<your-root-password>
DB_HOST=/cloudsql/<connection-name-from-step-11>

STORAGE_LOCATIONS="gcs"
STORAGE_GCS_DRIVER="gcs"
STORAGE_GCS_BUCKET=<your-bucket-name>

ADMIN_EMAIL="admin@example.com"
ADMIN_PASSWORD="localpassword"

KEY="secretkey"
SECRET="secret"
LOGGER_LEVELS="trace:DEBUG,debug:DEBUG,info:INFO,warn:WARNING,error:ERROR,fatal:CRITICAL"
`)],-1),ne=e("p",null,"Notes:",-1),le=e("ul",null,[e("li",null,[o("the value of "),e("code",null,"connectionName"),o(" from step 11 should be prefixed with "),e("code",null,"/cloudsql/"),o(" as the value of "),e("code",null,"DB_HOST")]),e("li",null,[e("code",null,"LOGGER_LEVELS"),o(" is optional, but makes Directus logs show up with correct level in Google Cloud Logging.")])],-1),se=e("ol",{start:"13"},[e("li",null,"Build your container Run these commands.")],-1),re=e("pre",null,[e("code",null,`docker build -t eu.gcr.io/<your-project-id>/directus .
gcloud auth configure-docker -q
docker push eu.gcr.io/<your-project-id>/directus
`)],-1),ae=e("p",null,"This will build the Docker container, authenticate your docker installation with Google Cloud Platform and push the container image to the container registry in your GCP project.",-1),ie=e("ol",{start:"14"},[e("li",null,"Deploy your container")],-1),ce=e("pre",null,[e("code",null,`gcloud run deploy directus \\
     --project "<your-project-id>" \\
     --image "eu.gcr.io/<your-project-id>/directus:latest" \\
     --region "europe-west1" \\
     --platform "managed" \\
     --allow-unauthenticated \\
     --add-cloudsql-instances "<database-connection-name-from-step11>"
`)],-1),ue=e("p",null,[o("Note: the value of "),e("code",null,"connectionName"),o(" from step 11 should be used as the value of "),e("code",null,"add-cloudsql-instances"),o(" without any prefix.")],-1),de=e("ol",{start:"15"},[e("li",null,[o("Done! The deploy command should\u2019ve told you the URL where you can access your Directus instance. You can login with "),e("code",null,"admin@example.com"),o(" and "),e("code",null,"localpassword"),o(".")])],-1),he=e("h2",{id:"additional-improvements"},[o("Additional improvements "),e("a",{class:"header-anchor",href:"#additional-improvements"},"#")],-1),pe=e("li",null,[o("You should make your CloudSQL instance only accessible through a private IP and VPC connector. This way only your current cloud project is able to access the database. More on this "),e("a",{href:"https://cloud.google.com/sql/docs/postgres/connect-run#private-ip",target:"_blank",rel:"noopener noreferrer"},"here"),o(".")],-1),ge=e("li",null,[o("You should not store your "),e("code",null,".env"),o(" file locally and build it into your Dockerfile. Ideally you should save your "),e("code",null,".env"),o(" file in "),e("a",{href:"https://cloud.google.com/secret-manager",target:"_blank",rel:"noopener noreferrer"},"Google Cloud Secret Manager"),o(" and in your CI/CD pipeline retrieve it and add it to your conainter. Or, even better, let the container pick the "),e("code",null,".env"),o(" up at runtime from Google Secret Manager.")],-1),_e=e("li",null,[o("Cloud Run typically allocates resources in a request context. Meaning async hooks etc will get drastically less CPU and memory, often even resulting in those processes not completing. You have two options: "),e("a",{href:"https://cloud.google.com/run/docs/configuring/cpu-allocation",target:"_blank",rel:"noopener noreferrer"},"CPU allocation"),o(" that is always allocated (which will increase cost) or handle everything in your extensions synchronously.")],-1),me=o("You could setup "),fe=o("caching"),ye=o(" using "),be=e("a",{href:"https://cloud.google.com/memorystore/docs/redis",target:"_blank",rel:"noopener noreferrer"},"Memorystore for Redis",-1),we=o("Since, by default, Google Cloud Run will scale back to zero instances of the container, it\u2019s impossible to use the "),ke=o("Schedule hooks"),Ce=o(" since there is no container to handle those schedules / cron jobs. Again you have two options: Set the minimum number of instances to 1, this will definitely increase cost as at least 1 container keeps on running 24/7. Or you could use "),Se=e("a",{href:"https://cloud.google.com/scheduler",target:"_blank",rel:"noopener noreferrer"},"Cloud Scheduler",-1),ve=o(" to schedule calls to custom endpoints which will do the tasks."),je={setup(De,{expose:a}){const s={title:"Google Cloud Platform",modularExtension:!1};return a({frontmatter:s}),(Re,Ge)=>{const n=r("router-link"),i=r("DocsWrapper");return c(),u(i,{frontmatter:s},{default:t(()=>[e("div",d,[h,p,g,_,m,f,y,b,w,k,C,S,v,D,R,G,A,e("ol",null,[j,e("li",null,[e("p",null,[E,l(n,{to:"/docs/getting-started/installation/manual"},{default:t(()=>[T]),_:1})])]),I]),L,q,O,P,N,x,B,W,M,Q,U,V,Y,K,z,F,H,J,X,Z,$,ee,oe,te,ne,le,se,re,ae,ie,ce,ue,de,he,e("ul",null,[pe,ge,_e,e("li",null,[me,l(n,{to:"/docs/configuration/config-options/#cache"},{default:t(()=>[fe]),_:1}),ye,be]),e("li",null,[we,l(n,{to:"/docs/extensions/hooks/#schedule"},{default:t(()=>[ke]),_:1}),Ce,Se,ve])])])]),_:1})}}};export{je as default};
