This website uses cookies

Our website, platform and/or any sub domains use cookies to understand how you use our services, and to improve both your experience and our marketing relevance.

Cloudways Velocity is here. Deploy Node.js apps on managed, predictable infrastructure. Start Hosting Now →

How to Deploy a Jev App on Cloudways Velocity

Updated on September 24, 2026

14 Min Read
Deploy jev app

Key Takeaways

  • Jev returns numerical toxicity, frustration, and spam scores instead of generating a text response, which is what makes real-time chat moderation possible.
  • The demo pairs a React frontend with a Node.js/Express backend that calls Jev’s Score primitive through the OpenRouter SDK.
  • Deploying the backend on Cloudways Velocity keeps it running as a persistent Node.js process, avoiding the cold-start delay that comes with serverless hosting.

Real-time AI moderation has two problems to deal with. The first is response time. Standard generative LLMs need time to generate and stream text, which isn’t ideal when a chat message needs to be checked before it reaches other users.

The second problem is the hosting environment. With serverless hosting, an application can go idle and need to start again when a new request arrives. That cold start can add another delay to the request.

In this tutorial, I’ll use Jev to handle the moderation decision without generating a text response, and I’ll run the application on Cloudways Velocity as a persistent Node.js environment.

The application will use a decoupled React and Node.js setup. The React frontend will send user input to the Node.js backend, which will send it to Jev and use the returned scores to check for toxicity and frustration.

Building a Chat Moderation App With Jev + Cloudways Velocity

A live chat is a good place to test a moderation system because the decision needs to happen before a message is shown to other users.

If someone sends a toxic message or posts a spam link, the backend needs to check it before allowing it into the chat. Waiting several seconds for a generated response would make the chat feel broken.

Since Jev returns numerical scores instead of generating a response, I can use those values directly in the moderation logic.

Before I started writing the application, I needed API access to the model. TypeSafe AI’s direct API was still behind an early-access waitlist, so I used OpenRouter instead.

I created an account on OpenRouter, opened the Credits page, and added $10 using my card.

OpenRouter Credits page showing $10 added to the account.

After that, I went to API Keys, copied the Default key, and had the API credentials I needed for the application.

My local Windows environment also has restrictions that prevent global software installations. Because of that, I used a standalone Node.js binary from:

C:\Users\abdulrehman\Downloads\node-v24.18.0-win-x64\node-v24.18.0-win-x64

I built the moderation dashboard in VS Code and ran the commands through Windows Command Prompt (CMD). The finished setup uses React for the frontend and Node.js for the backend, with the backend deployed on Cloudways Velocity.

Run Persistent Node.js Apps on Cloudways Velocity

Skip the cold starts that come with serverless functions. Deploy your Node.js backend on a persistent environment built for real-time workloads like AI moderation.

Step 1: Set Up the React Frontend

I wanted a simple way to see how quickly Jev responds, so I built a split-screen interface. The chat sits on the left, while an Admin Dashboard on the right shows the current Toxicity, Frustration, and Spam scores.

Since I didn’t have admin access on my Windows machine, I used Command Prompt (CMD) to set everything up. I first went to my user folder:

C:\Users\abdulrehman

Then I set the session PATH to point to my standalone Node.js folder.

I ran the setup commands one at a time. This is important because the npm create prompt can sometimes take the next command as part of the input if both are pasted together.

First, I prepared the environment and started the Vite installer.

cd C:\Users\abdulrehman

:: Set temporary PATH to use standalone Node binary
set PATH=C:\Users\abdulrehman\Downloads\node-v24.18.0-win-x64\node-v24.18.0-win-x64;%PATH%

npm create vite@latest chat-frontend -- --template react

When CMD asked Ok to proceed? (y), I entered y and pressed Enter. Once Vite finished creating the project, I moved into the new folder, installed the required packages, and started the development server.

cd chat-frontend
npm install
npm run dev

Terminal showing npm create vite, npm install, and npm run dev commands running.

The frontend was now running at:

http://localhost:5173/

Vite development server running at localhost 5173 in the terminal.

Since the development server keeps that CMD window busy, I opened another CMD window, set the Node.js path again, and launched Visual Studio Code (VS Code) to work on the project.

cd C:\Users\abdulrehman\chat-frontend
set PATH=C:\Users\abdulrehman\Downloads\node-v24.18.0-win-x64\node-v24.18.0-win-x64;%PATH%
code .

Opening the chat-frontend project in VS Code from Command Prompt.

Inside VS Code, I created ChatDashboard.jsx in the src folder.

Creating ChatDashboard.jsx inside the src folder in VS Code.

The main logic is fairly simple. When I send a message, the frontend marks it as evaluating and sends the message to the Node.js backend.

The backend sends the text to Jev and returns the risk scores. If the toxicity score goes above 90%, the frontend turns the message red and stops it from appearing in the chat.

Here is the React code I added to ChatDashboard.jsx:

import React, { useState } from 'react';

export default function ChatDashboard() {
  const [inputText, setInputText] = useState('');
  const [messages, setMessages] = useState([]);
  const [gauges, setGauges] = useState({ toxicity: 0, frustration: 0, spam: 0 });

  const sendMessage = async (e) => {
    e.preventDefault();
    if (!inputText.trim()) return;

    // Temporarily add message as "evaluating" to the UI
    const tempMsg = { text: inputText, status: 'evaluating' };
    setMessages((prev) => [...prev, tempMsg]);
    setInputText('');

    try {
      // Send payload to our backend (update this URL after deploying to Velocity)
      const response = await fetch('http://localhost:8080/api/moderate', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ message: tempMsg.text }),
      });
      
      const data = await response.json();
      
      // Update the visual admin gauges instantly based on Jev's numerical scores
      setGauges({
        toxicity: Math.round(data.scores.toxicity * 100),
        frustration: Math.round(data.scores.frustration * 100),
        spam: Math.round(data.scores.spam * 100)
      });

      // Update the message status based on the backend's strict block decision
      setMessages((prev) => 
        prev.map((msg, idx) => 
          idx === prev.length - 1 
            ? { ...msg, status: data.isBlocked ? 'blocked' : 'approved' } 
            : msg
        )
      );
    } catch (error) {
      console.error("Moderation error", error);
    }
  };

  return (
    <div style={{ display: 'flex', height: '100vh', fontFamily: 'sans-serif' }}>
      {/* Left Panel: Chat Interface */}
      <div style={{ flex: 1, padding: '2rem', borderRight: '1px solid #ccc' }}>
        <h2>Live Chat</h2>
        <div style={{ height: '400px', overflowY: 'auto', marginBottom: '1rem', padding: '1rem', background: '#f9f9f9' }}>
          {messages.map((msg, i) => (
            <div key={i} style={{ 
              padding: '10px', 
              margin: '5px 0', 
              background: msg.status === 'blocked' ? '#ffebee' : '#e8f5e9',
              color: msg.status === 'blocked' ? '#c62828' : '#2e7d32',
              borderLeft: `4px solid ${msg.status === 'blocked' ? 'red' : 'green'}`
            }}>
              {msg.status === 'blocked' ? '🚫 [BLOCKED: High Toxicity]' : msg.text}
            </div>
          ))}
        </div>
        <form onSubmit={sendMessage} style={{ display: 'flex' }}>
          <input 
            type="text" 
            value={inputText}
            onChange={(e) => setInputText(e.target.value)}
            placeholder="Type a message..."
            style={{ flex: 1, padding: '10px', fontSize: '16px' }}
          />
          <button type="submit" style={{ padding: '10px 20px', background: 'black', color: 'white' }}>Send</button>
        </form>
      </div>

      {/* Right Panel: Admin Moderation Gauges */}
      <div style={{ flex: 1, padding: '2rem', background: '#111', color: 'white' }}>
        <h2>System One Moderation</h2>
        <p>Jev real-time telemetry:</p>
        
        <div style={{ marginTop: '2rem' }}>
          <h3>Toxicity: {gauges.toxicity}%</h3>
          <div style={{ width: '100%', background: '#333', height: '20px' }}>
            <div style={{ width: `${gauges.toxicity}%`, background: 'red', height: '100%', transition: 'width 0.2s' }}></div>
          </div>

          <h3 style={{ marginTop: '1.5rem' }}>Frustration: {gauges.frustration}%</h3>
          <div style={{ width: '100%', background: '#333', height: '20px' }}>
            <div style={{ width: `${gauges.frustration}%`, background: 'orange', height: '100%', transition: 'width 0.2s' }}></div>
          </div>

          <h3 style={{ marginTop: '1.5rem' }}>Spam Probability: {gauges.spam}%</h3>
          <div style={{ width: '100%', background: '#333', height: '20px' }}>
            <div style={{ width: `${gauges.spam}%`, background: 'yellow', height: '100%', transition: 'width 0.2s' }}></div>
          </div>
        </div>
      </div>
    </div>
  );
}

chatdashboard jsx code
Next, I needed to replace Vite’s default App.jsx content so the new dashboard would load. I opened src/App.jsx and replaced the existing code with this

import ChatDashboard from './ChatDashboard'
function App() {
  return (
    <ChatDashboard />
  )
}
export default App

App.jsx updated to render the ChatDashboard component.

After saving the files, I opened http://localhost:5173/ in my browser. The split-screen dashboard loaded with the chat on one side and the moderation scores on the other.

Split-screen chat moderation dashboard loaded in the browser.

Step 2: Build the Node.js Backend With the Jev SDK

With the frontend working, I moved on to the Node.js backend. This service handles the requests from the React application and communicates with Jev.

I left the Vite server running, opened another CMD window, went back to my user folder, and created a separate directory for the backend.

cd C:\Users\abdulrehman
set PATH=C:\Users\abdulrehman\Downloads\node-v24.18.0-win-x64\node-v24.18.0-win-x64;%PATH%
mkdir chat-backend
cd chat-backend
npm init -y

Creating the chat-backend folder and running npm init in the terminal.

I then installed Express, CORS, dotenv, and the OpenRouter SDK:

npm install express cors dotenv @openrouter/sdk

Installing Express, CORS, dotenv, and the OpenRouter SDK.

I opened the chat-backend folder in VS Code by running:

code .

Before writing the server code, I needed to make one change to the Node.js project. A new Node project uses CommonJS by default, while my server code uses ES module imports.

I opened package.json and added:

"type": "module"

My updated code looked like this:

{
  "name": "chat-backend",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "start": "node server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@typesafe-ai/sdk": "^0.6.0",
    "cors": "^2.8.6",
    "dotenv": "^18.0.3",
    "express": "^5.2.1"
  }
}

GitHub repository created and ready for the chat-backend project.
After that, I created a .env file in the same folder and added my OpenRouter API key and server port there instead of putting the key directly into the JavaScript file.

OPENROUTER_API_KEY=sk-or-v1-your-actual-key-here
PORT=8080

The main part of the backend is the Jev scoring setup. Rather than sending Jev a question such as “Is this message toxic?” and asking it to return an answer, I used the SDK’s Score primitive.

This lets me send the chat message as the input and get numerical probabilities for toxicity, frustration, and spam in the same request.

I created server.js in VS Code and added the backend code:

import express from 'express';
import cors from 'cors';
import dotenv from 'dotenv';
import { OpenRouter } from '@openrouter/sdk';

// Load environment variables from .env
dotenv.config();

const app = express();
app.use(cors());
app.use(express.json());

// Initialize the OpenRouter client
const openrouter = new OpenRouter({
  apiKey: process.env.OPENROUTER_API_KEY
});

app.post('/api/moderate', async (req, res) => {
  const { message } = req.body;

  try {
    // Send the state payload to Jev using OpenRouter's Decisions API
    const decision = await openrouter.alpha.decisions.create({
      decisionsRequest: {
        model: "typesafe/jev-1.13",
        state: `User message: "${message}"`,
        questions: {
          toxicity: {
            type: "score",
            instructions: "How toxic, offensive, or harmful is this message?",
            criteria: ["Safe and benign", "Highly toxic or offensive"]
          },
          frustration: {
            type: "score",
            instructions: "How frustrated or angry does the user sound?",
            criteria: ["Calm", "Extremely frustrated or angry"]
          },
          spam: {
            type: "score",
            instructions: "What is the probability this message is spam or a promotion?",
            criteria: ["Normal conversation", "Blatant spam or promotion"]
          }
        }
      }
    });

    const answers = decision.answers;

    // Extract the numerical scores (0.0 to 1.0) from Jev's answers
    const scores = {
      toxicity: answers.toxicity?.score ?? 0,
      frustration: answers.frustration?.score ?? 0,
      spam: answers.spam?.score ?? 0
    };

    // Strict backend logic: Flag to block if toxicity is 90% or higher
    const isBlocked = scores.toxicity >= 0.90;

    // Instantly return the structured scores and block decision to React
    res.json({ scores, isBlocked });
  } catch (error) {
    console.error("Jev Evaluation Error:", error);
    res.status(500).json({ error: "Moderation failed" });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Moderation service running on port ${PORT}`));

server.js backend code with the Jev scoring setup in VS Code.

I tested the server locally with:

node server.js

Node server.js running and listening on port 8080.

The server started on port 8080, so I moved on to testing the moderation logic.

First, I sent a normal message:

“Hello everyone, excited to be here!”

The telemetry values didn’t move.

Telemetry gauges staying low after a normal, non-toxic message.

Then I tried:

“I’m mad, I hate it, really angry”

The frustration score jumped almost immediately.

Frustration score jumping up after an angry test message.

I also tested a highly toxic message. The scores increased, and the frontend blocked the message and displayed the red warning instead.

To measure the response time, I updated ChatDashboard.jsx and wrapped the API request with performance.now(). This let me show the request time directly on the dashboard.

Here is the updated code:

import React, { useState } from 'react';

export default function ChatDashboard() {
  const [inputText, setInputText] = useState('');
  const [messages, setMessages] = useState([]);
  const [gauges, setGauges] = useState({ toxicity: 0, frustration: 0, spam: 0 });
  
  // NEW: Added state to track latency
  const [latency, setLatency] = useState(null);

  const sendMessage = async (e) => {
    e.preventDefault();
    if (!inputText.trim()) return;

    // Temporarily add message as "evaluating" to the UI
    const tempMsg = { text: inputText, status: 'evaluating' };
    setMessages((prev) => [...prev, tempMsg]);
    setInputText('');

    try {
      // NEW: Start the latency timer
      const startTime = performance.now();

      // Send payload to our backend (update this URL after deploying to Velocity)
      const response = await fetch('http://localhost:8080/api/moderate', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ message: tempMsg.text }),
      });
      
      const data = await response.json();
      
      // NEW: Stop the timer and calculate round-trip milliseconds
      const endTime = performance.now();
      setLatency(Math.round(endTime - startTime));

      // Update the visual admin gauges instantly based on Jev's numerical scores
      setGauges({
        toxicity: Math.round(data.scores.toxicity * 100),
        frustration: Math.round(data.scores.frustration * 100),
        spam: Math.round(data.scores.spam * 100)
      });

      // Update the message status based on the backend's strict block decision
      setMessages((prev) => 
        prev.map((msg, idx) => 
          idx === prev.length - 1 
            ? { ...msg, status: data.isBlocked ? 'blocked' : 'approved' } 
            : msg
        )
      );
    } catch (error) {
      console.error("Moderation error", error);
    }
  };

  return (
    <div style={{ display: 'flex', height: '100vh', fontFamily: 'sans-serif' }}>
      {/* Left Panel: Chat Interface */}
      <div style={{ flex: 1, padding: '2rem', borderRight: '1px solid #ccc' }}>
        <h2>Live Chat</h2>
        <div style={{ height: '400px', overflowY: 'auto', marginBottom: '1rem', padding: '1rem', background: '#f9f9f9' }}>
          {messages.map((msg, i) => (
            <div key={i} style={{ 
              padding: '10px', 
              margin: '5px 0', 
              background: msg.status === 'blocked' ? '#ffebee' : '#e8f5e9',
              color: msg.status === 'blocked' ? '#c62828' : '#2e7d32',
              borderLeft: `4px solid ${msg.status === 'blocked' ? 'red' : 'green'}`
            }}>
              {msg.status === 'blocked' ? '🚫 [BLOCKED: High Toxicity]' : msg.text}
            </div>
          ))}
        </div>
        <form onSubmit={sendMessage} style={{ display: 'flex' }}>
          <input 
            type="text" 
            value={inputText}
            onChange={(e) => setInputText(e.target.value)}
            placeholder="Type a message..."
            style={{ flex: 1, padding: '10px', fontSize: '16px' }}
          />
          <button type="submit" style={{ padding: '10px 20px', background: 'black', color: 'white' }}>Send</button>
        </form>
      </div>

      {/* Right Panel: Admin Moderation Gauges */}
      <div style={{ flex: 1, padding: '2rem', background: '#111', color: 'white' }}>
        <h2>System One Moderation</h2>
        <p>Jev real-time telemetry:</p>
        
        {/* NEW: Render the latency timer on the screen */}
        <p style={{ color: '#4ade80', fontSize: '14px', marginTop: '-10px', marginBottom: '20px' }}>
          {latency ? `⚡ Processed in ${latency}ms` : '⚡ Awaiting input...'}
        </p>
        
        <div style={{ marginTop: '2rem' }}>
          <h3>Toxicity: {gauges.toxicity}%</h3>
          <div style={{ width: '100%', background: '#333', height: '20px' }}>
            <div style={{ width: `${gauges.toxicity}%`, background: 'red', height: '100%', transition: 'width 0.2s' }}></div>
          </div>

          <h3 style={{ marginTop: '1.5rem' }}>Frustration: {gauges.frustration}%</h3>
          <div style={{ width: '100%', background: '#333', height: '20px' }}>
            <div style={{ width: `${gauges.frustration}%`, background: 'orange', height: '100%', transition: 'width 0.2s' }}></div>
          </div>

          <h3 style={{ marginTop: '1.5rem' }}>Spam Probability: {gauges.spam}%</h3>
          <div style={{ width: '100%', background: '#333', height: '20px' }}>
            <div style={{ width: `${gauges.spam}%`, background: 'yellow', height: '100%', transition: 'width 0.2s' }}></div>
          </div>
        </div>
      </div>
    </div>
  );
}

After sending several messages, I was seeing Jev process each request in roughly 300-400ms.

Dashboard showing a Jev response latency of roughly 300 to 400 milliseconds.

This is incredibly fast compared to using standard conversational AI models like GPT-4o, which often take 2 to 4 seconds to evaluate text and return a rigidly formatted JSON response for this kind of moderation task.

Step 3: Deploy the Application on Cloudways Velocity

Now that the app was working locally, I was ready to deploy it. I wanted the Node.js backend to stay running instead of relying on a serverless function that could shut down when there was no traffic.

For this project, I used Cloudways Velocity as the hosting environment for the backend.

Commit the Code Using CMD

I already had a GitHub repository ready for the project.

GitHub repository created and ready for the chat-backend project.

Before pushing the code, I opened the chat-backend folder in VS Code and created a .gitignore file. I added node_modules and .env so the dependencies and my OpenRouter API key wouldn’t be committed to the repository.

.gitignore file with node_modules and .env added.

Next, I opened the CMD window where the backend was running and stopped the server with Ctrl + C, followed by Enter.

I then moved into the backend directory:

cd chat-backend

Finally, I ran the Git commands to add, commit, and push the project to GitHub.

git init
git add .
git commit -m "Initial commit of Jev moderation backend"
git branch -M main
git remote add origin https://github.com/abdulrehman293/JEV-Cloudways-Velocity-chat-backend.git
git push -u origin main

Git init, add, and commit commands running in the terminal.

Git push command completing and the code uploading to GitHub.

Connect the App to Velocity

In the Cloudways Velocity dashboard, I clicked Add Application and selected the Node.js Velocity server where I wanted to run the backend.

Cloudways Velocity dashboard Add Application option.

Selecting the Node.js Velocity server to run the backend.

Pull the Code From GitHub

I connected my GitHub account to Cloudways and selected the JEV-Cloudways-Velocity-chat-backend repository I created earlier.

Connecting a GitHub account to Cloudways Velocity.

Selecting the JEV-Cloudways-Velocity-chat-backend repository.

After selecting the repository and clicking Continue, Velocity detected the project and filled in the required build settings.

Cloudways Velocity detecting the project and filling in build settings.

I only needed to add two environment variables for the live application.

Add the Environment Variables

Under Environment Variables, I added:

  • OPENROUTER_API_KEY with my OpenRouter API key
  • PORT with the value 3000

Environment Variables section with OPENROUTER_API_KEY and PORT added.

This keeps the API key out of the repository while still making it available to the Node.js application when it runs on the server.

Deploy the Backend

I clicked Deploy.

Velocity pulled the repository, installed the Node.js dependencies, and started the Node.js process.

Cloudways Velocity deploying the Node.js backend.

A few minutes later, I had a live HTTPS URL for the backend:

https://nodejs-1666768-6691025.cloudwaysnodeapps.com/

Live HTTPS URL generated for the deployed Node.js backend.

Step 4: Connect the Local Frontend to the Live Backend

The backend was now running on Velocity, but the React frontend was still running on my computer. I needed to change the frontend so it would send requests to the live backend instead of localhost.

I went back to the VS Code window for the chat-frontend project and opened ChatDashboard.jsx.

I found the fetch call and replaced the localhost URL with the new Velocity URL. I kept the /api/moderate route at the end of the URL.

// Old local fetch
const response = await fetch('http://localhost:8080/api/moderate', {

// New live Velocity fetch
const response = await fetch('https://nodejs-1666768-6691025.cloudwaysnodeapps.com/api/moderate', {

ChatDashboard.jsx fetch call updated to point to the live Velocity URL.

After saving the file, I opened the frontend CMD window and ran:

npm run dev

Frontend development server restarted after updating the backend URL.

The React dashboard was then available again in my browser.

Step 5: Test the Live Backend

At this point, the frontend was running locally while the moderation API was running on Cloudways Velocity. I kept the setup this way so I could test the frontend and cloud backend separately.

I closed the earlier terminal window that was running the local backend. Then I opened the React app at:

http://localhost:5173/

Test a Normal Message

I started with a simple message:

“Hey everyone, excited to be in the chat today!”

The message went through and appeared in the green chat feed. The Toxicity, Frustration, and Spam gauges were all low.

Normal message approved and appearing in the green chat feed with low gauge scores.

Test the Moderation Threshold

For the second test, I used:

“Your shoe collection is terrible, give me my money back right now you scammers!”

The request came back with 88% Toxicity and 100% Frustration.

I had set the toxicity limit to 90%, so 88% wasn’t enough to block the message. It was allowed into the chat.

Message scoring 88 percent toxicity and 100 percent frustration, allowed into the chat under the 90 percent block threshold.

The React app was still running on localhost, but the requests were now going to the Node.js application deployed on Velocity. The two tests went through without any connection issues, and the Jev scores showed up in the dashboard as expected.

Wrapping Up!

Testing the deployed application demonstrates the exact speed of this architecture. Entering high-toxicity text into the React chat interface triggers an immediate visual response on the telemetry gauges, analyzing and approving or blocking the message before it renders in the UI.

Pairing a low-latency decision model with a persistent Node.js environment on Cloudways Velocity provides a robust, real-time moderation system capable of handling production workloads without the latency constraints of traditional generative AI.

Deploy Your Own Real-Time Node.js App on Velocity

Connect your Git repo, add your environment variables, and go live with a persistent backend built for low-latency workloads.

Q. Do I need direct access from TypeSafe AI to use Jev?

No. I used OpenRouter to access Jev with an API key.

Q. Why use a System One model instead of a standard generative LLM for moderation?

For this use case, I only need the moderation scores. A generative model returns text that the application would then need to interpret, while Jev gives me the numerical scores directly.

Q. How are code updates deployed on Cloudways Velocity?

The application is connected to a GitHub branch. After I push a change, Velocity can pull the updated code, build the application, and deploy the new version.

Share your opinion in the comment section. COMMENT NOW

Share This Article

Start Growing with Cloudways Today.

Our Clients Love us because we never compromise on these

Abdul Rehman

Abdul is a tech-savvy, coffee-fuelled, and creatively driven marketer who loves keeping up with the latest software updates and tech gadgets. He's also a skilled technical writer who can explain complex concepts simply for a broad audience. Abdul enjoys sharing his knowledge of the Cloud industry through user manuals, documentation, and blog posts.

×

Webinar: How to Get 100% Scores on Core Web Vitals

Join Joe Williams & Aleksandar Savkovic on 29th of March, 2021.

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Want to Experience the Cloudways Platform in Its Full Glory?

Take a FREE guided tour of Cloudways and see for yourself how easily you can manage your server & apps on the leading cloud-hosting platform.

Start my tour