Creating apps (deployments? services?) with the CLI

Noob… apologies!

Koyeb is interesting.

I’ve a strong preference for CLI > UI.

I was able to deploy an app using the UI but would prefer to do this using the CLI.

It’s unclear to me:

  1. Whether it’s possible to use the CLI to deploy an app
  2. Deploy an application uses app init flags. Is the CLI fully-equivalent to the UI for app deployment?
  3. Is there an app spec (YAML?) that fully defines apps and can be used with the CLI?
  4. After deploying an app, I can app list, service list and deployment list; the difference between these resource types is unclear (documented?)

My primary cloud platform is Google but I deploy to Azure, Linode, Vultr and Fly also.

hey!

We are currently working on the CLI to improve it. Currently it’s working, but the experience using it is far from ideal.

At the moment, it is possible to deploy applications with koyeb app init or koyeb app create then koyeb service create.

For examples, you can run the help subcommand or checkout our custom github action which wraps the CLI.

It is not possible to have an app spec that defines the application but it’s a great idea!

Regarding the differences between the app, service and deployment:

  • services are inside applications, and can communicate between each other directly. For example, if the services api and worker are in the same application, they can use the private domains api or worker to reach each other.
  • the deployment defines how your application runs: if you update a service with a scale of 4, we will create a new deployment for your service, and 4 “regional deployments”: one per instance running your service. With koyeb deployment list , you can see a revision history of your service.
1 Like

This is very helpful, thank you!

I did not see this explanation in the service’s documentation and think, if it’s not currently explained there, that it would be helpful information to include.

1 Like

Hi Julien, any progress on this ?
Currently I am trying hard to use the CLI in my yml action file to deploy a Dockerized app (a duckdb/fastapi/prometheus combo).

I have a hard time :dizzy_face:

koyeb service deploy --config ./koyeb.yaml --app duckdb-spawn --token ${{ secrets.KOYEB_TOKEN }}

name: duckdb-spawn
services:
  - name: api
    type: docker
    docker:
      image: ${{ secrets.DOCKER_HUB_USERNAME }}/${{ env.DOCKER_IMAGE_NAME }}:latest
    ports:
      - name: http
        port: 8000
    env:
      - name: DATABASE_URL
        value: /data/duckdb_spawn.db
    resources:
      cpu: 500m
      memory: 512Mi
    regions:
      - fra

Can’t find a definitive guide or doc on setup of github actions to deploy on Koyeb.

Help or pointer appreciated.

regards

Pas si vite – I succeeded in creating an app, but not in spawning the service from my docker image.

Can you help me set the right Github action?

I have the image properly built in my docker hub (and it is accessed through my docker secrets) .

It seems that although I specify a type : docker in the koyeb.yaml file, the koyeb services create expects some github information.

I get the following errors in the Github action run

koyeb apps create duckdb-spawn --config ./koyeb.yaml --token *** --debug
  koyeb services create duckdb-spawn/api --config ./koyeb.yaml --token *** --debug
  shell: /usr/bin/bash -e {0}
  env:
    DOCKER_HUB_USERNAME: ***
    DOCKER_IMAGE_NAME: duckdb-spawn
    KOYEB_APP_NAME: duckdb-spawn
time="2024-11-21T17:22:41Z" level=debug msg="Using config file: ./koyeb.yaml"
time="2024-11-21T17:22:42Z" level=debug msg="Using host: app.koyeb.com using https"
time="2024-11-21T17:22:42Z" level=debug msg="Unable to find setter SetHelp on *koyeb.CreateApp\n"
time="2024-11-21T17:22:42Z" level=debug msg="========== HTTP request ==========\nPOST /v1/apps HTTP/1.1\r\nHost: app.koyeb.com\r\nUser-Agent: koyeb-cli/5.3.0\r\nContent-Length: 24\r\nAccept: */*\r\nAuthorization: <HIDDEN, add --debug-full to show the token>\nContent-Type: application/json; charset=utf-8\r\nAccept-Encoding: gzip\r\n\r\n{\"name\":\"duckdb-spawn\"}\n\n========== end of request ==========\n"
time="2024-11-21T17:22:42Z" level=debug msg="========== HTTP response ==========\nHTTP/2.0 200 OK\r\nCache-Control: no-cache, no-store\r\nContent-Type: application/json\r\nDate: Thu, 21 Nov 2024 17:22:42 GMT\r\nPragma: no-cache\r\nStrict-Transport-Security: max-age=15724800; includeSubDomains\r\nVary: Accept-Encoding\r\nX-Ratelimit-Limit: 30, 30;w=60\r\nX-Ratelimit-Remaining: 29\r\nX-Ratelimit-Reset: 18\r\n\r\n{\"app\":{\"id\":\"2635617b-9ed4-4491-a10e-3bcd18be5129\",\"name\":\"duckdb-spawn\",\"organization_id\":\"907de27c-acaa-4573-bf2a-3a7d34ce0baa\",\"created_at\":\"2024-11-21T17:22:42.595491Z\",\"updated_at\":\"2024-11-21T17:22:42.595491Z\",\"started_at\":null,\"succeeded_at\":null,\"paused_at\":null,\"resumed_at\":null,\"terminated_at\":null,\"status\":\"STARTING\",\"messages\":[\"App starting.\"],\"version\":\"0\",\"domains\":[{\"id\":\"6e23c8fb-9327-4e7e-ba6a-0b7524c0a58d\",\"organization_id\":\"907de27c-acaa-4573-bf2a-3a7d34ce0baa\",\"name\":\"duckdb-spawn-dealexmachina-b837970c.koyeb.app\",\"created_
ID      	NAME        	STATUS  	DOMAINS                                          	CREATED AT          
2635617b	duckdb-spawn	STARTING	["duckdb-spawn-dealexmachina-b837970c.koyeb.app"]	21 Nov 24 17:22 UTC	
time="2024-11-21T17:22:42Z" level=debug msg="Using config file: ./koyeb.yaml"
time="2024-11-21T17:22:42Z" level=debug msg="Using host: app.koyeb.com using https"
time="2024-11-21T17:22:42Z" level=debug msg="========== HTTP request ==========\nGET /v1/apps?limit=100&offset=0 HTTP/1.1\r\nHost: app.koyeb.com\r\nUser-Agent: koyeb-cli/5.3.0\r\nAccept: */*\r\nAuthorization: <HIDDEN, add --debug-full to show the token>\nAccept-Encoding: gzip\r\n\r\n\n========== end of request ==========\n"
time="2024-11-21T17:22:43Z" level=debug msg="========== HTTP response ==========\nHTTP/2.0 200 OK\r\nCache-Control: no-cache, no-store\r\nContent-Type: application/json\r\nDate: Thu, 21 Nov 2024 17:22:43 GMT\r\nPragma: no-cache\r\nStrict-Transport-Security: max-age=15724800; includeSubDomains\r\nVary: Accept-Encoding\r\nX-Ratelimit-Limit: 200, 200;w=60\r\nX-Ratelimit-Remaining: 199\r\nX-Ratelimit-Reset: 17\r\n\r\n{\"apps\":[{\"id\":\"bd805dc9-47f7-4e9e-acce-88d0ad6499c6\",\"name\":\"koyeb-db-preview-app\",\"organization_id\":\"907de27c-acaa-4573-bf2a-3a7d34ce0baa\",\"updated_at\":\"2024-05-07T07:00:59.767248Z\",\"created_at\":\"2024-05-07T07:00:30.667617Z\",\"domains\":[{\"id\":\"ae95a7e5-b488-4020-90dc-e695efb9f5c6\",\"organization_id\":\"907de27c-acaa-4573-bf2a-3a7d34ce0baa\",\"name\":\"koyeb-db-preview-app-dealexmachina-592b4f11.koyeb.app\",\"created_at\":\"2024-05-07T07:00:30.682003Z\",\"updated_at\":\"2024-05-07T07:00:30.682003Z\",\"status\":\"ACTIVE\",\"type\":\"AUTOASSIGNED\",\"app_id\":\"bd805dc9-47f7
time="2024-11-21T17:22:43Z" level=debug msg="========== HTTP request ==========\nGET /v1/apps/2635617b-9ed4-4491-a10e-3bcd18be5129 HTTP/1.1\r\nHost: app.koyeb.com\r\nUser-Agent: koyeb-cli/5.3.0\r\nAccept: */*\r\nAuthorization: <HIDDEN, add --debug-full to show the token>\nAccept-Encoding: gzip\r\n\r\n\n========== end of request ==========\n"
time="2024-11-21T17:22:43Z" level=debug msg="========== HTTP response ==========\nHTTP/2.0 200 OK\r\nCache-Control: no-cache, no-store\r\nContent-Type: application/json\r\nDate: Thu, 21 Nov 2024 17:22:43 GMT\r\nPragma: no-cache\r\nStrict-Transport-Security: max-age=15724800; includeSubDomains\r\nVary: Accept-Encoding\r\nX-Ratelimit-Limit: 200, 200;w=60\r\nX-Ratelimit-Remaining: 199\r\nX-Ratelimit-Reset: 17\r\n\r\n{\"app\":{\"id\":\"2635617b-9ed4-4491-a10e-3bcd18be5129\",\"name\":\"duckdb-spawn\",\"organization_id\":\"907de27c-acaa-4573-bf2a-3a7d34ce0baa\",\"created_at\":\"2024-11-21T17:22:42.595491Z\",\"updated_at\":\"2024-11-21T17:22:42.595491Z\",\"started_at\":null,\"succeeded_at\":null,\"paused_at\":null,\"resumed_at\":null,\"terminated_at\":null,\"status\":\"STARTING\",\"messages\":[\"App starting.\"],\"version\":\"0\",\"domains\":[{\"id\":\"6e23c8fb-9327-4e7e-ba6a-0b7524c0a58d\",\"organization_id\":\"907de27c-acaa-4573-bf2a-3a7d34ce0baa\",\"name\":\"duckdb-spawn-dealexmachina-b837970c.koyeb.app\",\"creat
time="2024-11-21T17:22:43Z" level=debug msg="========== HTTP request ==========\nPOST /v1/services HTTP/1.1\r\nHost: app.koyeb.com\r\nUser-Agent: koyeb-cli/5.3.0\r\nContent-Length: 371\r\nAccept: */*\r\nAuthorization: <HIDDEN, add --debug-full to show the token>\nContent-Type: application/json; charset=utf-8\r\nAccept-Encoding: gzip\r\n\r\n{\"app_id\":\"2635617b-9ed4-4491-a10e-3bcd18be5129\",\"definition\":{\"git\":{\"branch\":\"main\"},\"instance_types\":[{\"scopes\":[\"region:was\"],\"type\":\"nano\"}],\"name\":\"api\",\"ports\":[{\"port\":8000,\"protocol\":\"http\"}],\"regions\":[\"was\"],\"routes\":[{\"path\":\"/\",\"port\":8000}],\"scalings\":[{\"max\":1,\"min\":1,\"scopes\":[\"region:was\"],\"targets\":[]}],\"skip_cache\":false,\"strategy\":{},\"type\":\"WEB\"}}\n\n========== end of request ==========\n"
time="2024-11-21T17:22:43Z" level=debug msg="========== HTTP response ==========\nHTTP/2.0 400 Bad Request\r\nContent-Length: 150\r\nCache-Control: no-cache, no-store\r\nContent-Type: application/json\r\nDate: Thu, 21 Nov 2024 17:22:43 GMT\r\nPragma: no-cache\r\nStrict-Transport-Security: max-age=15724800; includeSubDomains\r\nX-Ratelimit-Limit: 30, 30;w=60\r\nX-Ratelimit-Remaining: 29\r\nX-Ratelimit-Reset: 17\r\n\r\n{\"status\":400,\"code\":\"invalid_argument\",\"message\":\"Validation error\",\"fields\":[{\"field\":\"definition.git.repository\",\"description\":\"cannot be blank\"}]}\n========== end of response ==========\n"
❌ Error while creating the service: the Koyeb API returned an error 400: Validation error
🔎 Additional details
Field definition.git.repository: cannot be blank
🏥 How to solve the issue?
Fix the request, and try again
🕦 The original error was:
400 Bad Request 
Error: Process completed with exit code 1.

On a variation note

I will try to use Pulumi in the action yaml to deploy to Koyeb.

Hi @jeanbapt, I’m not sure what are trying to do here.
Are you trying to use github action GitHub - koyeb/action-git-deploy ?

1 Like

Hello Lukasz,

I was trying to use the Koyeb CLI (with a yaml file) in a action script, but I visibly failed.

I will have a look at using the action-git-deploy. I presume it works to deploy docker images …

Otherwise I will change my strategy and deploy everything from pulumi using the koyeb_pulumi lib.

Regards,

JB

This is solved, thank you !

1 Like

How did you solve it? Pls share so others can learn too.

I will . I need to streamline / fix a few things first. Thank you again.

Automate the docker image build and deploy to Koyeb

I was trying to automate the build and the deployment of my app docker image to koyeb through the koyeb.deploy.ym action file in my GitHub. I set up correctly my secrets (for docker hub and koyeb in Github secrets.).
I was hitting an error again and again, trying to install the koyeb cli to be used by the runner.
Ok my mistake was to try to use the Koyeb CLI directly in actions.

and took the DOCKER-REPO-SECRET that was created apparently when I created the service with the first push.

No I can smoothly deploy my updates from my local dev to my staging in koyeb.

This is how the file ended up :

name: Deploy to Koyeb

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2

      - name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_HUB_USERNAME }}
          password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}

      - name: Build and push Docker image
        id: docker_build
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: ${{ secrets.DOCKER_HUB_USERNAME }}/duckdb-spawn:${{ github.sha }}
# Did this as I got errors from the script trying to access the image. 
      - name: Debug - Verify image exists
        run: |
          echo "Checking image: ${{ secrets.DOCKER_HUB_USERNAME }}/duckdb-spawn:${{ github.sha }}"
          docker pull ${{ secrets.DOCKER_HUB_USERNAME }}/duckdb-spawn:${{ github.sha }}
          echo "Image details:"
          docker inspect ${{ secrets.DOCKER_HUB_USERNAME }}/duckdb-spawn:${{ github.sha }}

      - name: Configure Koyeb
        uses: koyeb-community/koyeb-actions@v2
        with:
          api_token: "${{ secrets.KOYEB_API_TOKEN }}"

      - name: Deploy on Koyeb
        uses: koyeb/action-git-deploy@v1
        with:
          app-name: duckdb-spawn
          service-name: api
          service-type: web
          docker: docker.io/${{ secrets.DOCKER_HUB_USERNAME }}/duckdb-spawn:${{ github.sha }}
          service-env: DATABASE_URL=/data/duckdb_spawn.db,PYTHONUNBUFFERED=1,LOG_LEVEL=info,ENVIRONMENT=staging
          service-ports: 8000:http
          service-routes: /:8000
          service-instance-type: nano
          service-regions: fra
          service-checks: 8000:http:/monitoring/health
          docker-private-registry-secret: DOCKER_REPO_SECRET
          

The project is public (it is some fiddling aroun data products) at

1 Like

Thank you for sharing it!