Cron Schedule Run
isaac-hinman
PROOP

6 months ago

Perhaps I have misunderstood, but I was hoping to use the "Cron Schedule

Run the service according to the specified cron schedule" as an alternative to serverless:

https://docs.railway.com/reference/app-sleeping

Some things than can prevent a service from being put to sleep -

  • Keeping active database connections open, such as a database connection pooler.

  • Making requests to other services in the same project over the private network

Unfortunately, our application (Appsmith) does keep active database connections open. Ideally we'd want it to run serverless, but the next best option would just be "turn the service off over night". At least that way we could save ~35% on totally wasted compute.

Is that possible with "Cron Schedule Run", or have I misunderstood the feature? Is there any way to "only run this service between 7:00 and 23:00 each day, and shut off/scale to zero overnight"?

Solved$20 Bounty

17 Replies

brody
EMPLOYEE

6 months ago

Hello,

There is no way to run a service between set times, Cron Schedules are only for one-off jobs, if you use it for an application that stays active like a webserver it will never shut down.

Best,
Brody


Status changed to Awaiting User Response Railway 6 months ago


isaac-hinman
PROOP

6 months ago

Thanks, that is unfortunate.


Status changed to Awaiting Railway Response Railway 6 months ago


Status changed to Solved isaac-hinman 6 months ago


isaac-hinman
PROOP

6 months ago

Brody, just to double check, there is no Railway API endpoint to stop a service, is there? I have seen mention of API ability to restart a service, but not to stop one. We could self-service this need via cron jobs if we are indeed able to stop (or scale to zero) a service programmatically.


Status changed to Awaiting Railway Response Railway 6 months ago


isaac-hinman
PROOP

6 months ago

Right, just tried via the GraphQL API and after some fiddling ended up with this error:

"Invalid multi-region config: [

{

"code": "too_small",

"minimum": 1,

"type": "number",

"inclusive": true,

"message": "Number must be greater than or equal to 1",

"path": [

"us-east2",

"numReplicas"

]

}

]"

This seems like a ridiculous constraint – zero replicas IS a valid state and users should be allowed to assign to it.


brody
EMPLOYEE

6 months ago

Zero replicas is not a valid state, but you can stop the service with the deploymentStop mutation.


Status changed to Awaiting User Response Railway 6 months ago


isaac-hinman
PROOP

6 months ago

Ah thanks, that is a very helpful tip. Would you be able to help me with a Railway Function that takes a SERVICE_ID, RAILWAY_API_TOKEN, and ENVIRONMENT_ID as env vars and calls deploymentStop?

Been banging my head against this one for an hour now. I think it would be nice if Railway ultimately offered "stop" and "start" services as primitives to be run on cron patterns.


Status changed to Awaiting Railway Response Railway 6 months ago


isaac-hinman
PROOP

6 months ago

Or is there already a template for "Stop a service" and "Start a service" somewhere?


brody
EMPLOYEE

6 months ago

I unfortunately cannot offer coding assistance here, but I can offer you the graphql playground that will help you build the mutations needed -

https://railway.com/graphiql

I (or another one of my teammates) can also open this thread up to our bounty program so that fellow community members could offer you help here if you would like?


Status changed to Awaiting User Response Railway 6 months ago


isaac-hinman
PROOP

6 months ago

Sure, it seems reasonable to put a bounty on "cron start a service" and "cron stop a service" if Railway cannot offer this functionality out of the box, nor help support/develop it.

Seems like a template that would take someone familiar with the GraphQL API about 5 min to do.


Status changed to Awaiting Railway Response Railway 6 months ago


I agree, a reasonable bounty indeed.

Pushed this to the community, may the best solution win


Status changed to Awaiting User Response Railway 6 months ago


This thread has been marked as public for community involvement, as it does not contain any sensitive or personal information. Any further activity in this thread will be visible to everyone.

Status changed to Open angelo-railway 6 months ago


isaac-hinman
PROOP

6 months ago

Was this post deleted?

Attachments


isaac-hinman

Was this post deleted?

sim
FREE

6 months ago

I do not know but can you manually stop the service via the API or CLI on a schedule?


sim

I do not know but can you manually stop the service via the API or CLI on a schedule?

isaac-hinman
PROOP

5 months ago

That's essentially the question being asked. I was not able to figure out how to achieve it with the GraphQL API.


androidquartz
HOBBY

5 months ago

import { GraphQLClient } from "graphql-request";

const client = new GraphQLClient("https://backboard.railway.com/graphql/v2", {
  headers: {
    Authorization: `Bearer ${Bun.env.RAILWAY_TOKEN}`,
  },
});

const serviceId = Bun.env.TARGET_RAILWAY_SERVICE_ID;
const environmentId = Bun.env.TARGET_RAILWAY_ENVIRONMENT_ID;

const getLatestDeployment = async () => {
  const data = await client.request(
    `
    query getLatestDeployment($serviceId: String!, $environmentId: String!) {
      serviceInstance(environmentId: $environmentId, serviceId: $serviceId) {
        latestDeployment {
          id
        }
      }
    }
  `,
    { serviceId, environmentId }
  );

  return (data as any).serviceInstance.latestDeployment.id;
};

const stopDeployment = async (deploymentId: string) => {
  await client.request(
    `
    mutation sleepDeployment($id: String!) {
      deploymentStop(id: $id)
    }
  `,
    { id: deploymentId }
  );
};

const main = async () => {
  const latestDeploymentId = await getLatestDeployment();

  console.log("Found deployment with id", latestDeploymentId);

  await stopDeployment(latestDeploymentId);
};

await main();

Deploy a function with this code, add env vars:

{
  "RAILWAY_TOKEN": "<TOKEN>",
  "TARGET_RAILWAY_ENVIRONMENT_ID": "${{target-service-name.RAILWAY_ENVIRONMENT_ID}}",
  "TARGET_RAILWAY_SERVICE_ID": "${{target-service-name.RAILWAY_SERVICE_ID}}"
}

For some reason when I used a project token scoped to production environment it gave an not authorized error, but it worked fine with an account token

{
  "errors": [
    {
      "message": "Not Authorized",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "serviceInstance"
      ],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR"
      },
      "traceId": "6544843598974348261"
    }
  ],
  "data": null
}

androidquartz

import { GraphQLClient } from "graphql-request"; const client = new GraphQLClient("https://backboard.railway.com/graphql/v2", { headers: { Authorization: `Bearer ${Bun.env.RAILWAY_TOKEN}`, }, }); const serviceId = Bun.env.TARGET_RAILWAY_SERVICE_ID; const environmentId = Bun.env.TARGET_RAILWAY_ENVIRONMENT_ID; const getLatestDeployment = async () => { const data = await client.request( ` query getLatestDeployment($serviceId: String!, $environmentId: String!) { serviceInstance(environmentId: $environmentId, serviceId: $serviceId) { latestDeployment { id } } } `, { serviceId, environmentId } ); return (data as any).serviceInstance.latestDeployment.id; }; const stopDeployment = async (deploymentId: string) => { await client.request( ` mutation sleepDeployment($id: String!) { deploymentStop(id: $id) } `, { id: deploymentId } ); }; const main = async () => { const latestDeploymentId = await getLatestDeployment(); console.log("Found deployment with id", latestDeploymentId); await stopDeployment(latestDeploymentId); }; await main();Deploy a function with this code, add env vars:{ "RAILWAY_TOKEN": "<TOKEN>", "TARGET_RAILWAY_ENVIRONMENT_ID": "${{target-service-name.RAILWAY_ENVIRONMENT_ID}}", "TARGET_RAILWAY_SERVICE_ID": "${{target-service-name.RAILWAY_SERVICE_ID}}" }For some reason when I used a project token scoped to production environment it gave an not authorized error, but it worked fine with an account token{ "errors": [ { "message": "Not Authorized", "locations": [ { "line": 2, "column": 3 } ], "path": [ "serviceInstance" ], "extensions": { "code": "INTERNAL_SERVER_ERROR" }, "traceId": "6544843598974348261" } ], "data": null }

isaac-hinman
PROOP

5 months ago

Thanks, any idea how to start a deployment? The desire is to turn deployments off for periods of time when not needed.


isaac-hinman

Thanks, any idea how to start a deployment? The desire is to turn deployments off for periods of time when not needed.

androidquartz
HOBBY

5 months ago

I think if you turn on serverless option it will automatically start when it receives a new connection. If not, you can use this mutation:

mutation restartDeployment($id: String!) {
  deploymentRestart(id: $id)
}

Here's an updated script to restart latest deployment:

import { GraphQLClient } from "graphql-request";

const client = new GraphQLClient("https://backboard.railway.com/graphql/v2", {
  headers: {
    Authorization: `Bearer ${Bun.env.RAILWAY_TOKEN}`,
  },
});

const serviceId = Bun.env.TARGET_RAILWAY_SERVICE_ID;
const environmentId = Bun.env.TARGET_RAILWAY_ENVIRONMENT_ID;

const getLatestDeployment = async () => {
  const data = await client.request(
    `
    query getLatestDeployment($serviceId: String!, $environmentId: String!) {
      serviceInstance(environmentId: $environmentId, serviceId: $serviceId) {
        latestDeployment {
          id
        }
      }
    }
  `,
    { serviceId, environmentId }
  );

  return (data as any).serviceInstance.latestDeployment.id;
};

const stopDeployment = async (deploymentId: string) => {
  await client.request(
    `
    mutation sleepDeployment($id: String!) {
      deploymentStop(id: $id)
    }
  `,
    { id: deploymentId }
  );
};

const restartDeployment = async (deploymentId: string) => {
  await client.request(
    `
    mutation restartDeployment($id: String!) {
      deploymentRestart(id: $id)
    }
  `,
    { id: deploymentId }
  );
};

const main = async () => {
  const latestDeploymentId = await getLatestDeployment();

  console.log("Found deployment with id", latestDeploymentId);

  // await stopDeployment(latestDeploymentId);
  await restartDeployment(latestDeploymentId);
};

await main();

androidquartz

I think if you turn on serverless option it will automatically start when it receives a new connection. If not, you can use this mutation:mutation restartDeployment($id: String!) { deploymentRestart(id: $id) }Here's an updated script to restart latest deployment:import { GraphQLClient } from "graphql-request"; const client = new GraphQLClient("https://backboard.railway.com/graphql/v2", { headers: { Authorization: `Bearer ${Bun.env.RAILWAY_TOKEN}`, }, }); const serviceId = Bun.env.TARGET_RAILWAY_SERVICE_ID; const environmentId = Bun.env.TARGET_RAILWAY_ENVIRONMENT_ID; const getLatestDeployment = async () => { const data = await client.request( ` query getLatestDeployment($serviceId: String!, $environmentId: String!) { serviceInstance(environmentId: $environmentId, serviceId: $serviceId) { latestDeployment { id } } } `, { serviceId, environmentId } ); return (data as any).serviceInstance.latestDeployment.id; }; const stopDeployment = async (deploymentId: string) => { await client.request( ` mutation sleepDeployment($id: String!) { deploymentStop(id: $id) } `, { id: deploymentId } ); }; const restartDeployment = async (deploymentId: string) => { await client.request( ` mutation restartDeployment($id: String!) { deploymentRestart(id: $id) } `, { id: deploymentId } ); }; const main = async () => { const latestDeploymentId = await getLatestDeployment(); console.log("Found deployment with id", latestDeploymentId); // await stopDeployment(latestDeploymentId); await restartDeployment(latestDeploymentId); }; await main();

isaac-hinman
PROOP

5 months ago

This appears to work, but the "stop a deployment" part actually surfaces as a crash, not a graceful stop. Ideally we'd not be getting emails once a day saying our service "crashed" when we shut it down intentionally, but I suppose that is down to Railway. Cheers!


Status changed to Solved chandrika 6 months ago


Loading...