<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Saif Ali</title>
    <description>The latest articles on DEV Community by Saif Ali (@sali_ac161a1b71406354896c).</description>
    <link>https://dev.to/sali_ac161a1b71406354896c</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3780544%2F353acf11-059d-471c-a779-825d98da6c00.png</url>
      <title>DEV Community: Saif Ali</title>
      <link>https://dev.to/sali_ac161a1b71406354896c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sali_ac161a1b71406354896c"/>
    <language>en</language>
    <item>
      <title>NEXUS AI - Claude Code Tutorial</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Sun, 09 Aug 2026 13:30:56 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/nexus-ai-claude-code-tutorial-19ml</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/nexus-ai-claude-code-tutorial-19ml</guid>
      <description>&lt;p&gt;Most Claude Code tutorials stop at "here's how to install it." That's like teaching someone to drive by showing them the ignition. This Claude Code tutorial goes further — you'll use it to build a real AI-powered app from scratch and deploy it to production, step by step.&lt;/p&gt;

&lt;p&gt;By the end you'll have a working document Q&amp;amp;A API and a live deployment URL. The whole thing takes about an afternoon.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Claude Code actually is (and why it's different)
&lt;/h2&gt;

&lt;p&gt;Claude Code is Anthropic's AI coding agent that runs in your terminal. Unlike copilot-style tools that suggest individual lines inside an editor, Claude Code operates at the project level — it reads your entire codebase, understands how files relate to each other, and makes multi-file changes with full context.&lt;/p&gt;

&lt;p&gt;The practical difference: you describe what you want to build, and Claude Code writes the code, runs commands, fixes errors, and iterates — without you switching between a chat window and your editor. It's AI-augmented development where the AI is a collaborator in your actual workflow, not a suggestion box beside it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it powerful for AI app development specifically:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It can generate boilerplate for FastAPI, Express, or any framework in seconds&lt;/li&gt;
&lt;li&gt;It writes tests alongside the code it generates&lt;/li&gt;
&lt;li&gt;It catches its own mistakes by running the code and reading error output&lt;/li&gt;
&lt;li&gt;It handles the tedious parts (CI config, requirements.txt, test scaffolding) while you focus on the actual problem&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Getting started: install and configure Claude Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @anthropic-ai/claude-code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Requires Node.js 18+. Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Authenticate
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On first run, Claude Code opens a browser window to authenticate with your Anthropic account. Once authenticated, it drops you into an interactive session in your current directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your first command
&lt;/h3&gt;

&lt;p&gt;Navigate to an empty project folder and try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-ai-app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;my-ai-app
claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the Claude Code prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Scaffold a FastAPI project with a single /health endpoint, a requirements.txt, and a .gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code will create the files, show you what it's doing, and confirm. This is the core interaction pattern: describe the outcome, let it execute.&lt;/p&gt;




&lt;h2&gt;
  
  
  Build a real AI app with Claude Code
&lt;/h2&gt;

&lt;p&gt;We're building a &lt;strong&gt;document Q&amp;amp;A API&lt;/strong&gt; — you upload a text document, ask questions about it, and get answers grounded in the document's content. It's a practical RAG (retrieval-augmented generation) pattern used in real products.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 — Scaffold the project
&lt;/h3&gt;

&lt;p&gt;In your Claude Code session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Create a FastAPI app for document Q&amp;amp;A. The app should:
&amp;gt; - Accept a POST /upload endpoint that takes a text file and stores it in memory
&amp;gt; - Accept a POST /ask endpoint that takes a document_id and a question, then answers using OpenAI gpt-4o-mini
&amp;gt; - Return answers in JSON with the answer text and a confidence field
&amp;gt; - Include a requirements.txt with fastapi, uvicorn, openai, and python-multipart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code will generate the full project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-ai-app/
├── main.py
├── requirements.txt
├── .gitignore
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It writes the entire &lt;code&gt;main.py&lt;/code&gt; — endpoints, in-memory document store, OpenAI call — in one pass.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 — Run it and fix errors
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Run the app locally with uvicorn and show me any errors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code executes &lt;code&gt;uvicorn main:app --reload&lt;/code&gt;, reads the output, and if there are import errors or missing packages it fixes them automatically. This loop — run, read error, fix — is where Claude Code earns its keep. You don't context-switch; it just handles it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 — Add real retrieval (not just stuffing the whole document)
&lt;/h3&gt;

&lt;p&gt;The naive version sends the entire document to the model on every question. That breaks on large files and wastes tokens. Ask Claude Code to improve it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; The current /ask endpoint sends the full document to OpenAI on every request.
&amp;gt; Refactor it to:
&amp;gt; - Split documents into 500-token chunks on upload
&amp;gt; - Use cosine similarity on TF-IDF vectors to find the top 3 relevant chunks
&amp;gt; - Only send those 3 chunks to OpenAI as context
&amp;gt; - Use numpy and sklearn for the vector operations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is multi-file, multi-concept work. Claude Code will update &lt;code&gt;main.py&lt;/code&gt;, add &lt;code&gt;sklearn&lt;/code&gt; and &lt;code&gt;numpy&lt;/code&gt; to &lt;code&gt;requirements.txt&lt;/code&gt;, and implement the chunking + retrieval logic coherently. It understands that changing the upload flow affects the query flow and handles both.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 — Write tests
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Write pytest tests for both endpoints. Include:
&amp;gt; - A test that uploads a sample document and verifies the document_id is returned
&amp;gt; - A test that uploads a document, then asks a question whose answer is clearly in the document
&amp;gt; - A test that asks about a document_id that doesn't exist and expects a 404
&amp;gt; Mock the OpenAI call so tests don't need a real API key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code generates &lt;code&gt;test_main.py&lt;/code&gt; with the exact structure you described, uses &lt;code&gt;pytest-mock&lt;/code&gt; for the OpenAI mock, and adds the test dependencies to &lt;code&gt;requirements.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Run them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Run the tests and fix any failures
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code runs &lt;code&gt;pytest&lt;/code&gt;, reads the output, and iterates until they pass. The critical detail: it doesn't just generate tests and hand them back — it runs them and closes the feedback loop.&lt;/p&gt;




&lt;h2&gt;
  
  
  The CLAUDE.md file: your project's AI instruction layer
&lt;/h2&gt;

&lt;p&gt;One of the most underused Claude Code features is &lt;code&gt;CLAUDE.md&lt;/code&gt; — a file in your project root that Claude Code reads at the start of every session. Think of it as a permanent briefing document for your AI collaborator.&lt;/p&gt;

&lt;p&gt;Create one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Create a CLAUDE.md for this project that documents:
&amp;gt; - The tech stack (FastAPI, OpenAI, sklearn)
&amp;gt; - The coding conventions we used (snake_case, type hints everywhere, docstrings on public functions)
&amp;gt; - The test setup (pytest, mock OpenAI calls)
&amp;gt; - What the /upload and /ask endpoints do
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this point on, any new Claude Code session on this project starts with full context. You don't re-explain the stack every time.&lt;/p&gt;

&lt;p&gt;A good &lt;code&gt;CLAUDE.md&lt;/code&gt; includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Project: Document Q&amp;amp;A API&lt;/span&gt;

&lt;span class="gu"&gt;## Stack&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; FastAPI + Uvicorn (Python 3.11)
&lt;span class="p"&gt;-&lt;/span&gt; OpenAI gpt-4o-mini for generation
&lt;span class="p"&gt;-&lt;/span&gt; sklearn TF-IDF + cosine similarity for retrieval
&lt;span class="p"&gt;-&lt;/span&gt; pytest + pytest-mock for testing

&lt;span class="gu"&gt;## Conventions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Type hints on all function signatures
&lt;span class="p"&gt;-&lt;/span&gt; Snake_case everywhere
&lt;span class="p"&gt;-&lt;/span&gt; Docstrings on all public functions

&lt;span class="gu"&gt;## Architecture&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Documents stored in-memory (dict keyed by UUID)
&lt;span class="p"&gt;-&lt;/span&gt; Chunks: 500 tokens, 50-token overlap
&lt;span class="p"&gt;-&lt;/span&gt; Top 3 chunks retrieved per query

&lt;span class="gu"&gt;## Running locally&lt;/span&gt;
uvicorn main:app --reload --port 8000

&lt;span class="gu"&gt;## Running tests&lt;/span&gt;
pytest -v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Deploy to production with the NEXUS AI CLI
&lt;/h2&gt;

&lt;p&gt;Your app is built and tested. Now get it live — no Dockerfile required.&lt;/p&gt;

&lt;p&gt;NEXUS AI detects your framework, builds the container for you, and deploys it. You push source code; NEXUS AI handles everything from there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 — Push source to GitHub
&lt;/h3&gt;

&lt;p&gt;Initialize a repo and push:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git init
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"initial: document Q&amp;amp;A API"&lt;/span&gt;
gh repo create my-ai-app &lt;span class="nt"&gt;--public&lt;/span&gt; &lt;span class="nt"&gt;--source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--push&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or push to an existing repo. The only requirement is that your &lt;code&gt;requirements.txt&lt;/code&gt; is at the project root — NEXUS AI uses it to detect that this is a Python app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 — Install the NEXUS AI CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Linux&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://nexusai.run/install.sh | bash

&lt;span class="c"&gt;# macOS&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://nexusai.run/install-mac.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7 — Deploy from source
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Log in to NEXUS AI&lt;/span&gt;
nexus auth login

&lt;span class="c"&gt;# Deploy directly from your GitHub repo — no Docker required&lt;/span&gt;
nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/your-org/my-ai-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; doc-qa-api &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--port&lt;/span&gt; 8000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; gcp_cloud_run

&lt;span class="c"&gt;# Add the OpenAI key as an encrypted secret&lt;/span&gt;
nexus secret create OPENAI_API_KEY &lt;span class="nt"&gt;--deployment&lt;/span&gt; doc-qa-api

&lt;span class="c"&gt;# Attach a custom domain&lt;/span&gt;
nexus domain add api.yourcompany.com &lt;span class="nt"&gt;--deployment&lt;/span&gt; doc-qa-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NEXUS AI clones your repo, detects the Python/FastAPI framework, builds a production container image, and deploys it. Within 2–3 minutes you have a live URL with TLS and autoscaling. Stream logs to verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy logs doc-qa-api &lt;span class="nt"&gt;--follow&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automate deploys with GitHub Actions
&lt;/h3&gt;

&lt;p&gt;Ask Claude Code to write the CI/CD config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;Write&lt;/span&gt; &lt;span class="err"&gt;a&lt;/span&gt; &lt;span class="err"&gt;GitHub&lt;/span&gt; &lt;span class="err"&gt;Actions&lt;/span&gt; &lt;span class="err"&gt;workflow&lt;/span&gt; &lt;span class="err"&gt;that&lt;/span&gt; &lt;span class="err"&gt;redeploys&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;NEXUS&lt;/span&gt; &lt;span class="err"&gt;AI&lt;/span&gt; &lt;span class="err"&gt;deployment&lt;/span&gt; &lt;span class="err"&gt;on&lt;/span&gt; &lt;span class="err"&gt;every&lt;/span&gt; &lt;span class="err"&gt;push&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;main.&lt;/span&gt;
&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s"&gt; Use NEXUSAI_TOKEN as a secret. The deployment name is doc-qa-api.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code generates a complete &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;. The workflow calls &lt;code&gt;nexus deploy redeploy doc-qa-api&lt;/code&gt; — NEXUS AI pulls the latest source, rebuilds the container, and rolls it out. Every push to &lt;code&gt;main&lt;/code&gt; goes to production automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Advanced Claude Code patterns for AI development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Multi-file refactoring
&lt;/h3&gt;

&lt;p&gt;Claude Code handles refactors that would take hours manually. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; The document store is currently an in-memory dict. Refactor it to use Redis so documents
&amp;gt; persist across server restarts. Update all references, add redis to requirements.txt,
&amp;gt; and update the CLAUDE.md architecture section.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It updates &lt;code&gt;main.py&lt;/code&gt;, &lt;code&gt;requirements.txt&lt;/code&gt;, and &lt;code&gt;CLAUDE.md&lt;/code&gt; in one coherent pass — and since NEXUS AI builds from source, you just push the changes and redeploy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debugging without context-switching
&lt;/h3&gt;

&lt;p&gt;When something breaks in production:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy logs doc-qa-api &lt;span class="nt"&gt;--tail&lt;/span&gt; 50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the error, paste it into Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Getting this error in production logs: [paste error]
&amp;gt; Find the root cause and fix it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code reads the relevant code, identifies the issue, and applies the fix — all without you manually tracing through stack traces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Claude Code for code review
&lt;/h3&gt;

&lt;p&gt;Before opening a PR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;&amp;gt; Review the changes in git diff HEAD~1 for:
&amp;gt; - Security issues (injection, hardcoded secrets, unsafe deserialization)
&amp;gt; - Missing input validation on the API endpoints
&amp;gt; - Performance issues in the chunking logic
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code runs the diff and produces a structured review with specific line references.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Claude Code mistakes to avoid
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Giving vague prompts.&lt;/strong&gt; "Make this better" produces mediocre output. "Refactor the chunking function to reduce memory allocation by processing tokens in a streaming fashion instead of loading the full document" produces a specific, actionable change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not using CLAUDE.md.&lt;/strong&gt; Without it, you re-explain your stack every session. Ten minutes setting it up saves hours over the life of a project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accepting the first output blindly.&lt;/strong&gt; Claude Code is fast, not infallible. Run the tests after every significant change. When they fail, let Claude Code fix them — that feedback loop is what makes it reliable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Letting it over-engineer.&lt;/strong&gt; Claude Code will sometimes propose abstractions you don't need. If you asked for a simple endpoint and got a three-layer architecture with an abstract repository pattern, push back: "Simplify this — no abstraction layers, just the endpoint and direct database calls."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not scoping the context.&lt;/strong&gt; In very large codebases, &lt;code&gt;claude&lt;/code&gt; in the root directory gives it the whole repo. For a focused change, navigate to the relevant subdirectory first. Smaller context = more precise output.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does Claude Code work with languages other than Python?
&lt;/h3&gt;

&lt;p&gt;Yes. Claude Code works with any language — TypeScript, Go, Rust, Ruby, Java. The same patterns apply: scaffold with a prompt, run it, let Claude Code fix errors. The &lt;code&gt;CLAUDE.md&lt;/code&gt; approach works especially well in polyglot repos where you need to document which parts use which language.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Claude Code safe to run on production codebases?
&lt;/h3&gt;

&lt;p&gt;Claude Code asks for confirmation before writing files or running commands. You control what it executes. For sensitive production repos, review the proposed changes before confirming — Claude Code shows you a diff before applying it. Never give it credentials directly; use environment variables and secrets managers.&lt;/p&gt;

&lt;h3&gt;
  
  
  How is Claude Code different from GitHub Copilot?
&lt;/h3&gt;

&lt;p&gt;Copilot autocompletes individual lines and functions inside an editor. Claude Code operates at the project level in the terminal — it understands the full codebase, can run code, read test output, and make coordinated multi-file changes. They're complementary: Copilot for keystroke-level suggestions, Claude Code for larger tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use Claude Code without an Anthropic account?
&lt;/h3&gt;

&lt;p&gt;No. Claude Code requires an Anthropic API key or Claude.ai Pro/Max subscription. Usage via the API is billed based on token consumption. The claude.ai subscription tiers include a monthly usage allocation.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's the best way to handle large codebases?
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;.claudeignore&lt;/code&gt; (same syntax as &lt;code&gt;.gitignore&lt;/code&gt;) to exclude directories that aren't relevant to your current task — &lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;dist&lt;/code&gt;, &lt;code&gt;venv&lt;/code&gt;, build artifacts. This keeps Claude Code's context focused on what matters and reduces token usage.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you built
&lt;/h2&gt;

&lt;p&gt;Start to finish: a document Q&amp;amp;A API scaffolded by Claude Code, with chunked retrieval, pytest coverage, and a live deployment on NEXUS AI — all without writing a Dockerfile or touching a browser.&lt;/p&gt;

&lt;p&gt;That's AI-augmented development in practice. Claude Code handled the scaffolding, boilerplate, tests, and debugging loop. You handled the architecture decisions and product requirements. The result ships faster and has better test coverage than the same work done manually.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://dev.to/docs"&gt;NEXUS AI CLI&lt;/a&gt; handles the deployment side of this workflow. Install it, run &lt;code&gt;nexus auth login&lt;/code&gt;, and your next Claude Code-built app is one command away from production.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>nexusai</category>
      <category>ai</category>
      <category>web</category>
    </item>
    <item>
      <title>NEXUS AI Audit</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Sun, 09 Aug 2026 13:29:25 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/nexus-ai-audit-491g</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/nexus-ai-audit-491g</guid>
      <description>&lt;h1&gt;
  
  
  Audit logs and compliance: what gets recorded and why
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Published:&lt;/strong&gt; April 21, 2026&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Security · Compliance&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time:&lt;/strong&gt; 15 minutes&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Author:&lt;/strong&gt; NEXUS AI Team&lt;/p&gt;



&lt;p&gt;A production incident happens at 2:47 AM. You wake up to alerts. By the time you open your laptop, the question isn't "what failed" — your monitoring already told you that. The question is "who changed what, when, and from where?"&lt;/p&gt;

&lt;p&gt;Without audit logs, that question takes hours. With audit logs, it takes minutes.&lt;/p&gt;

&lt;p&gt;NEXUS AI records 42 distinct event types across 7 categories — every authentication attempt, every secret operation, every deployment action, every permission change. This post covers exactly what gets recorded, what each severity level means, how retention works, and how the audit log maps to the compliance controls your team, auditors, and regulators care about.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why audit logs exist — and why most teams underinvest in them
&lt;/h2&gt;

&lt;p&gt;Audit logs are not a debugging tool. They are an accountability system.&lt;/p&gt;

&lt;p&gt;The difference matters. A debugging tool helps you understand why software broke. An accountability system answers a harder set of questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did an authorized person take this action?&lt;/li&gt;
&lt;li&gt;From a recognized location?&lt;/li&gt;
&lt;li&gt;At a time that makes sense?&lt;/li&gt;
&lt;li&gt;On the resource they were supposed to touch?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the questions a security incident forces you to answer — and the questions compliance auditors ask during a review. Every minute you spend reconstructing context from application logs and git history is a minute your accountability system didn't pay for itself.&lt;/p&gt;

&lt;p&gt;NEXUS AI's audit log is designed to answer accountability questions in seconds, not hours.&lt;/p&gt;


&lt;h2&gt;
  
  
  The event catalog: 42 event types, 7 categories
&lt;/h2&gt;

&lt;p&gt;Every event stored in the audit log has a named type. Here is the complete catalog.&lt;/p&gt;
&lt;h3&gt;
  
  
  Authentication events
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;What triggered it&lt;/th&gt;
&lt;th&gt;Default severity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LOGIN_SUCCESS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Successful sign-in via password or OAuth&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LOGIN_FAILED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Failed sign-in attempt (wrong password, invalid token)&lt;/td&gt;
&lt;td&gt;WARNING&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LOGOUT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Explicit sign-out&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;REGISTER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New account created&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TOKEN_REFRESH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Session token refreshed&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TOKEN_EXPIRED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Session token expired and was rejected&lt;/td&gt;
&lt;td&gt;WARNING&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Authentication events are the first line of accountability. A single &lt;code&gt;LOGIN_FAILED&lt;/code&gt; is noise. Fifteen &lt;code&gt;LOGIN_FAILED&lt;/code&gt; events from the same IP in 90 seconds is a brute-force signal — recorded, flagged, and available for your SIEM in real time.&lt;/p&gt;
&lt;h3&gt;
  
  
  Deployment events
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;What triggered it&lt;/th&gt;
&lt;th&gt;Default severity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DEPLOYMENT_CREATED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New deployment provisioned&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DEPLOYMENT_UPDATED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deployment configuration changed&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DEPLOYMENT_STARTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stopped deployment brought back online&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DEPLOYMENT_STOPPED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Running deployment halted&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DEPLOYMENT_DELETED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deployment permanently removed&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DEPLOYMENT_FAILED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build or container start failed&lt;/td&gt;
&lt;td&gt;ERROR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DEPLOYMENT_SCALED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replica count changed&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every deploy action is tied to the actor who triggered it — a user email, a token ID, or both. When a deploy fires at 3 AM, you know whether it was a CI token, a scheduled job, or a human who shouldn't have been working at 3 AM.&lt;/p&gt;
&lt;h3&gt;
  
  
  Security events
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;What triggered it&lt;/th&gt;
&lt;th&gt;Default severity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DOCKERFILE_VALIDATION_FAILED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Submitted Dockerfile failed safety checks&lt;/td&gt;
&lt;td&gt;WARNING&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RATE_LIMIT_EXCEEDED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;API caller exceeded request rate limits&lt;/td&gt;
&lt;td&gt;WARNING&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UNAUTHORIZED_ACCESS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Request rejected due to insufficient permissions&lt;/td&gt;
&lt;td&gt;WARNING&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SUSPICIOUS_ACTIVITY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Behavioral anomaly detected by the security monitor&lt;/td&gt;
&lt;td&gt;CRITICAL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CONTAINER_ESCAPE_ATTEMPT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Container process attempted to break isolation&lt;/td&gt;
&lt;td&gt;CRITICAL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;SUSPICIOUS_ACTIVITY&lt;/code&gt; and &lt;code&gt;CONTAINER_ESCAPE_ATTEMPT&lt;/code&gt; are the two events that trigger an immediate alert. They are never demoted to WARNING or lower.&lt;/p&gt;
&lt;h3&gt;
  
  
  Resource events
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;What triggered it&lt;/th&gt;
&lt;th&gt;Default severity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RESOURCE_LIMIT_EXCEEDED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deployment exceeded its CPU, memory, or storage limit&lt;/td&gt;
&lt;td&gt;WARNING&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HIGH_CPU_USAGE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Container CPU usage crossed threshold&lt;/td&gt;
&lt;td&gt;WARNING&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HIGH_MEMORY_USAGE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Container memory usage crossed threshold&lt;/td&gt;
&lt;td&gt;WARNING&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Resource events are included in the audit log — not just the metrics system — because they tell the story of capacity-related incidents. A deployment that gets quietly OOM-killed at 4 PM on a Tuesday has a paper trail.&lt;/p&gt;
&lt;h3&gt;
  
  
  Administrative events
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;What triggered it&lt;/th&gt;
&lt;th&gt;Default severity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;USER_CREATED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New team member account created or invited&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;USER_DELETED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Team member removed from the organization&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PERMISSION_CHANGED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Role or scope assignment changed&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CONFIG_CHANGED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Organization-level configuration updated&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Administrative events answer the access-review question: not just who has access now, but who granted it, when, and to whom. Every &lt;code&gt;PERMISSION_CHANGED&lt;/code&gt; event records the before and after state in the &lt;code&gt;details&lt;/code&gt; JSON field.&lt;/p&gt;
&lt;h3&gt;
  
  
  Project events
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;What triggered it&lt;/th&gt;
&lt;th&gt;Default severity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PROJECT_CREATED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New project created&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PROJECT_UPDATED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Project metadata or settings changed&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PROJECT_DELETED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Project and all its deployments deleted&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Secret and vault events
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;What triggered it&lt;/th&gt;
&lt;th&gt;Default severity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SECRET_CREATED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New secret stored in the vault&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SECRET_UPDATED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secret value changed&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SECRET_ROTATED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secret rotated (new value, same name)&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SECRET_DELETED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secret removed from the vault&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SECRET_REVEALED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secret value decrypted for display&lt;/td&gt;
&lt;td&gt;WARNING&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SECRET_LISTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secret names listed (values not returned)&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SECRET_RUNTIME_ACCESSED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secret decrypted for container injection at deploy time&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;SECRET_REVEALED&lt;/code&gt; is marked WARNING by design. The vault never returns plaintext values through normal operations — if a &lt;code&gt;SECRET_REVEALED&lt;/code&gt; event fires, an admin explicitly requested a value display. That is worth noting.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SECRET_RUNTIME_ACCESSED&lt;/code&gt; records every time a secret is decrypted for injection into a running container. On a high-frequency redeploy environment, this creates a complete timeline of which secrets were active in which container instances.&lt;/p&gt;
&lt;h3&gt;
  
  
  Database intelligence events
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;What triggered it&lt;/th&gt;
&lt;th&gt;Default severity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DATABASE_ACCESSED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;External database connection established&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DATABASE_MODIFIED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Schema change or DDL applied to external database&lt;/td&gt;
&lt;td&gt;WARNING&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DATABASE_QUERY_EXECUTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SQL query executed against an external database source&lt;/td&gt;
&lt;td&gt;INFO&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;DATABASE_MODIFIED&lt;/code&gt; is promoted to WARNING because schema changes are high-impact and difficult to reverse. Every DDL statement executed through NEXUS AI's Database Intelligence layer — whether applied directly or via a proposed fix — produces a record.&lt;/p&gt;


&lt;h2&gt;
  
  
  The audit log record
&lt;/h2&gt;

&lt;p&gt;Every event writes a single record to the &lt;code&gt;audit_logs&lt;/code&gt; table. Here is what that record looks like in full:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"f3c8a21b-4d9e-4a7f-b6c1-e2d8f0a3b591"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eventType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DEPLOYMENT_CREATED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"INFO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"usr_01HX9..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"organizationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"org_01HX9..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ipAddress"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"140.82.114.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userAgent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nexusapp-cli/2.0.0 node/20.11.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resourceId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dep_api-prod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resourceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deployment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DEPLOYMENT_CREATED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deploymentName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api-prod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ghcr.io/org/api:sha-abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AWS_APP_RUNNER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tokenId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tok_01HX9..."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"errorMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-21T09:17:05.000Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every field is intentional:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Why it exists&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique record identifier — stable reference for incident tickets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;eventType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Machine-readable event name — filterable, indexable, SIEM-parseable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;severity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;INFO / WARNING / ERROR / CRITICAL — drives alerting and dashboards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;userId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The human actor (null if action was taken by a token with no session)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;organizationId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tenant boundary — logs are always org-scoped; cross-tenant reads are impossible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ipAddress&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Source IP of the request — critical for geolocation anomaly detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;userAgent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CLI version, browser, SDK — surfaces automation vs. human access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;resourceId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The specific resource acted upon — deployments, secrets, users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;resourceType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The category of resource — enables filtering by type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;action&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Human-readable description of what happened&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;details&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Freeform JSON — event-specific context (image tag, region, old role, new role)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;success&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whether the action completed successfully&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;errorMessage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If &lt;code&gt;success&lt;/code&gt; is false, why it failed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timestamp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;UTC timestamp of the event — stored with millisecond precision&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;details&lt;/code&gt; field is where event-specific context lives. A &lt;code&gt;PERMISSION_CHANGED&lt;/code&gt; record includes the old role and new role. A &lt;code&gt;SECRET_RUNTIME_ACCESSED&lt;/code&gt; record includes the deployment ID and the name (not value) of the secret. A &lt;code&gt;LOGIN_FAILED&lt;/code&gt; record includes the email attempted.&lt;/p&gt;




&lt;h2&gt;
  
  
  Severity levels
&lt;/h2&gt;

&lt;p&gt;NEXUS AI uses four severity levels. They control how an event is stored, surfaced, and retained.&lt;/p&gt;

&lt;h3&gt;
  
  
  INFO
&lt;/h3&gt;

&lt;p&gt;Normal system operation. Every successful deploy, login, and secret list operation lands here. INFO events are written to the database and available for query, but they do not trigger any alert. They form the baseline — the record of what "normal" looks like.&lt;/p&gt;

&lt;h3&gt;
  
  
  WARNING
&lt;/h3&gt;

&lt;p&gt;Something worth noting. Failed logins, rate limit hits, secret reveal operations, and Dockerfile validation failures are WARNING events. They do not indicate a breach, but they indicate conditions that — in volume or combination — warrant investigation. Five &lt;code&gt;LOGIN_FAILED&lt;/code&gt; events is noise. Fifty in ten minutes is a pattern your SIEM should surface.&lt;/p&gt;

&lt;h3&gt;
  
  
  ERROR
&lt;/h3&gt;

&lt;p&gt;An action failed in a way that requires attention. &lt;code&gt;DEPLOYMENT_FAILED&lt;/code&gt; is an ERROR. These events are logged to the application error stream in addition to the database, so they appear in your observability pipeline immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  CRITICAL
&lt;/h3&gt;

&lt;p&gt;Immediate action required. Only two event types default to CRITICAL: &lt;code&gt;SUSPICIOUS_ACTIVITY&lt;/code&gt; and &lt;code&gt;CONTAINER_ESCAPE_ATTEMPT&lt;/code&gt;. CRITICAL events trigger the alerting pipeline — currently logging to the critical error stream, with email, Slack, and PagerDuty integrations on the roadmap. CRITICAL events are also exempt from the standard 90-day retention purge. They are kept indefinitely, regardless of plan.&lt;/p&gt;




&lt;h2&gt;
  
  
  The security score
&lt;/h2&gt;

&lt;p&gt;NEXUS AI's security monitor computes a rolling 100-point security score for each organization, recalculated across a configurable window (default: 7 days).&lt;/p&gt;

&lt;p&gt;The score starts at 100 and deducts based on event volume:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event category&lt;/th&gt;
&lt;th&gt;Deduction per occurrence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Failed login (&lt;code&gt;LOGIN_FAILED&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;−2 points&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate limit exceeded (&lt;code&gt;RATE_LIMIT_EXCEEDED&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;−1 point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dockerfile validation failure (&lt;code&gt;DOCKERFILE_VALIDATION_FAILED&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;−5 points&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Critical security event (&lt;code&gt;severity: CRITICAL&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;−10 points&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A score of 100 means no adverse events in the review window. A score of 60 means something is worth investigating. A score below 40 should trigger an active security review.&lt;/p&gt;

&lt;p&gt;The score is visible in the NEXUS AI dashboard under Settings → Security and is available via the API at &lt;code&gt;GET /api/audit/security-summary&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Retention policy
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Retention window&lt;/th&gt;
&lt;th&gt;CRITICAL event retention&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Starter&lt;/td&gt;
&lt;td&gt;90 days&lt;/td&gt;
&lt;td&gt;Indefinite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;90 days&lt;/td&gt;
&lt;td&gt;Indefinite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;td&gt;Configurable (env: &lt;code&gt;AUDIT_LOG_RETENTION_DAYS&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Indefinite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise On-Prem&lt;/td&gt;
&lt;td&gt;You own the database&lt;/td&gt;
&lt;td&gt;You own the database&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The retention job runs daily at 3:30 AM UTC. It purges records older than the retention window — except CRITICAL events, which are never automatically purged.&lt;/p&gt;

&lt;p&gt;On Enterprise, set &lt;code&gt;AUDIT_LOG_RETENTION_DAYS&lt;/code&gt; to any positive integer. Regulated industries typically set this to 365 (HIPAA minimum) or 2555 (7-year financial records requirement).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example: 365-day retention for HIPAA workloads&lt;/span&gt;
&lt;span class="nv"&gt;AUDIT_LOG_RETENTION_DAYS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;365
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Enterprise On-Prem, you bring your own PostgreSQL cluster. Audit logs live in your database, under your retention and backup policies, with no data leaving your infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Accessing your audit logs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Dashboard
&lt;/h3&gt;

&lt;p&gt;The audit log viewer is at Settings → Audit Logs in the NEXUS AI dashboard. Filter by event type, severity, date range, and user. Paginated, searchable, exportable.&lt;/p&gt;

&lt;h3&gt;
  
  
  API
&lt;/h3&gt;

&lt;p&gt;The audit log API is at &lt;code&gt;/api/audit/logs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get the last 50 logs&lt;/span&gt;
curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://nexusai.run/api/audit/logs?limit=50"&lt;/span&gt;

&lt;span class="c"&gt;# Filter by event type&lt;/span&gt;
curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://nexusai.run/api/audit/logs?eventType=SECRET_UPDATED&amp;amp;limit=100"&lt;/span&gt;

&lt;span class="c"&gt;# Filter by severity&lt;/span&gt;
curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://nexusai.run/api/audit/logs?severity=WARNING&amp;amp;limit=100"&lt;/span&gt;

&lt;span class="c"&gt;# Date range (ISO 8601)&lt;/span&gt;
curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://nexusai.run/api/audit/logs?startDate=2026-04-01T00:00:00Z&amp;amp;endDate=2026-04-21T23:59:59Z"&lt;/span&gt;

&lt;span class="c"&gt;# Security summary for the last 7 days&lt;/span&gt;
curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://nexusai.run/api/audit/security-summary?days=7"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CSV export
&lt;/h3&gt;

&lt;p&gt;Export up to 10,000 log records as a CSV file — ready to upload to your SIEM, compliance platform, or auditor portal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Export last 30 days to CSV&lt;/span&gt;
curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://nexusai.run/api/audit/export?startDate=2026-03-21T00:00:00Z"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; audit-logs-march-2026.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CSV includes all fields: timestamp, event type, severity, user ID, IP address, action, success, error message, resource ID, and resource type. The &lt;code&gt;details&lt;/code&gt; JSON field is serialized as a string in the CSV export.&lt;/p&gt;

&lt;h3&gt;
  
  
  Available filter endpoints
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /api/audit/logs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Query logs with filters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /api/audit/export&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Download CSV (up to 10,000 records)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /api/audit/security-summary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;7-day security summary and score&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /api/audit/security-metrics&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Real-time threat metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /api/audit/event-types&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all 42 event types and 4 severity levels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /api/audit/user-activity/:userId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;30-day activity summary for a specific user&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Audit logs in a real incident
&lt;/h2&gt;

&lt;p&gt;Here is how audit logs actually look during an incident response workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; At 11:42 PM, a production deployment stops unexpectedly. The on-call engineer opens the audit log.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Filter for recent deployment events on the affected resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://nexusai.run/api/audit/logs?eventType=DEPLOYMENT_STOPPED&amp;amp;limit=10"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eventType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DEPLOYMENT_STOPPED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"INFO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tokenId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tok_01HX9..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deploymentName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api-prod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"triggeredBy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"access_token"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ipAddress"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"198.51.100.22"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-21T23:42:17.000Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No &lt;code&gt;userId&lt;/code&gt; — the stop was triggered by an Access Token, not a human session. &lt;code&gt;tokenId&lt;/code&gt; identifies which token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Cross-reference the token ID against the token list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus token list &lt;span class="nt"&gt;--json&lt;/span&gt; | jq &lt;span class="s1"&gt;'.[] | select(.id == "tok_01HX9...")'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; The token was named &lt;code&gt;github-actions-prod&lt;/code&gt;. It has &lt;code&gt;deploy:write&lt;/code&gt; scope. But the GitHub Actions workflow that uses it is only supposed to trigger redeployments, not stops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Pull the full recent activity for that token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://nexusai.run/api/audit/logs?limit=20"&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  jq &lt;span class="s1"&gt;'.data.logs[] | select(.details.tokenId == "tok_01HX9...")'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; The token was used from IP &lt;code&gt;198.51.100.22&lt;/code&gt;. Your CI/CD pipeline runs from &lt;code&gt;140.82.114.0/24&lt;/code&gt;. This IP is outside that range.&lt;/p&gt;

&lt;p&gt;The token was compromised. You revoke it immediately, rotate secrets, and have a complete timeline of every action it took — from the first legitimate use to the unauthorized stop. The entire investigation took 11 minutes.&lt;/p&gt;

&lt;p&gt;Without audit logs: that same investigation would have required GitHub Actions logs, cloud provider logs, and a manual timeline reconstruction. Best case: 90 minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Compliance mapping
&lt;/h2&gt;

&lt;p&gt;NEXUS AI's audit log maps directly to the access-control and audit requirements in the major compliance frameworks. This is not a marketing table — these are the specific control IDs your auditor will check.&lt;/p&gt;

&lt;h3&gt;
  
  
  HIPAA
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;HIPAA requirement&lt;/th&gt;
&lt;th&gt;Control ID&lt;/th&gt;
&lt;th&gt;NEXUS AI coverage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Access control — unique user identification&lt;/td&gt;
&lt;td&gt;§164.312(a)(2)(i)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;userId&lt;/code&gt; on every record; token-based access uses &lt;code&gt;tokenId&lt;/code&gt; in &lt;code&gt;details&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit controls — hardware, software, procedural mechanisms&lt;/td&gt;
&lt;td&gt;§164.312(b)&lt;/td&gt;
&lt;td&gt;42 event types, append-only log, 90–365 day retention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automatic logoff — session inactivity termination&lt;/td&gt;
&lt;td&gt;§164.312(a)(2)(iii)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;TOKEN_EXPIRED&lt;/code&gt; event records session termination&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encryption and decryption — PHI protection&lt;/td&gt;
&lt;td&gt;§164.312(a)(2)(iv)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SECRET_RUNTIME_ACCESSED&lt;/code&gt; records every secret decryption event&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Person or entity authentication — verify identity before granting access&lt;/td&gt;
&lt;td&gt;§164.312(d)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;LOGIN_FAILED&lt;/code&gt; events surface failed authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  SOC 2 Type II
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SOC 2 criterion&lt;/th&gt;
&lt;th&gt;Trust Service Criterion&lt;/th&gt;
&lt;th&gt;NEXUS AI coverage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Logical access controls&lt;/td&gt;
&lt;td&gt;CC6.1&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PERMISSION_CHANGED&lt;/code&gt;, &lt;code&gt;USER_CREATED&lt;/code&gt;, &lt;code&gt;USER_DELETED&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System access authorization&lt;/td&gt;
&lt;td&gt;CC6.2&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;LOGIN_SUCCESS&lt;/code&gt;, &lt;code&gt;LOGIN_FAILED&lt;/code&gt;, &lt;code&gt;UNAUTHORIZED_ACCESS&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User registration and de-provisioning&lt;/td&gt;
&lt;td&gt;CC6.3&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;USER_CREATED&lt;/code&gt;, &lt;code&gt;USER_DELETED&lt;/code&gt;, &lt;code&gt;PERMISSION_CHANGED&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restricting access to data in transit&lt;/td&gt;
&lt;td&gt;CC6.7&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SECRET_RUNTIME_ACCESSED&lt;/code&gt;, &lt;code&gt;DATABASE_ACCESSED&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring of system components&lt;/td&gt;
&lt;td&gt;CC7.2&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;HIGH_CPU_USAGE&lt;/code&gt;, &lt;code&gt;HIGH_MEMORY_USAGE&lt;/code&gt;, &lt;code&gt;RESOURCE_LIMIT_EXCEEDED&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incident detection and reporting&lt;/td&gt;
&lt;td&gt;CC7.3&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SUSPICIOUS_ACTIVITY&lt;/code&gt;, &lt;code&gt;CONTAINER_ESCAPE_ATTEMPT&lt;/code&gt; (CRITICAL, alerted immediately)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  GDPR
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GDPR requirement&lt;/th&gt;
&lt;th&gt;Article&lt;/th&gt;
&lt;th&gt;NEXUS AI coverage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Records of processing activities&lt;/td&gt;
&lt;td&gt;Art. 30&lt;/td&gt;
&lt;td&gt;Complete event log with timestamp, actor, resource, and outcome&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accountability — demonstrate compliance&lt;/td&gt;
&lt;td&gt;Art. 5(2)&lt;/td&gt;
&lt;td&gt;Append-only log with no modification capability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data access requests — who accessed what&lt;/td&gt;
&lt;td&gt;Art. 15&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;user-activity/:userId&lt;/code&gt; endpoint for per-user activity reports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Breach notification — detect and respond within 72 hours&lt;/td&gt;
&lt;td&gt;Art. 33&lt;/td&gt;
&lt;td&gt;CRITICAL events alerted immediately; &lt;code&gt;SUSPICIOUS_ACTIVITY&lt;/code&gt; surfaces breach signals&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  PCI DSS (v4.0)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PCI DSS requirement&lt;/th&gt;
&lt;th&gt;Control&lt;/th&gt;
&lt;th&gt;NEXUS AI coverage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Track and monitor access to cardholder data&lt;/td&gt;
&lt;td&gt;Req. 10.2&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;DATABASE_ACCESSED&lt;/code&gt;, &lt;code&gt;DATABASE_QUERY_EXECUTED&lt;/code&gt; for connected payment databases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Record user access to audit trails&lt;/td&gt;
&lt;td&gt;Req. 10.2.1&lt;/td&gt;
&lt;td&gt;All 42 event types record &lt;code&gt;userId&lt;/code&gt; or &lt;code&gt;tokenId&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Record privileged access&lt;/td&gt;
&lt;td&gt;Req. 10.2.1.b&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PERMISSION_CHANGED&lt;/code&gt; records role elevations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retain audit log history for at least 12 months&lt;/td&gt;
&lt;td&gt;Req. 10.7&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;AUDIT_LOG_RETENTION_DAYS=365&lt;/code&gt; on Enterprise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Review logs daily&lt;/td&gt;
&lt;td&gt;Req. 10.6&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GET /api/audit/logs&lt;/code&gt; with date filter; exportable to SIEM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Integrating with your SIEM
&lt;/h2&gt;

&lt;p&gt;NEXUS AI does not require a native SIEM integration — the export API and JSON query endpoint are designed to plug into any pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Datadog:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Pull last hour of WARNING+ events and ship to Datadog&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://nexusai.run/api/audit/logs?severity=WARNING&amp;amp;startDate=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nt"&gt;-v-1H&lt;/span&gt; &lt;span class="s1"&gt;'+%Y-%m-%dT%H:%M:%SZ'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  jq &lt;span class="s1"&gt;'.data.logs[]'&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; event&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://http-intake.logs.datadoghq.com/api/v2/logs"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"DD-API-KEY: &lt;/span&gt;&lt;span class="nv"&gt;$DD_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Grafana / Loki:&lt;/strong&gt; Ship the JSON response from &lt;code&gt;/api/audit/logs&lt;/code&gt; via a log shipper (Promtail, Alloy) configured to poll the endpoint on a scheduled interval.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Splunk / SIEM:&lt;/strong&gt; Use the CSV export endpoint (&lt;code&gt;/api/audit/export&lt;/code&gt;) on a scheduled basis and ingest via Splunk's file monitor input.&lt;/p&gt;

&lt;p&gt;On the Enterprise On-Prem plan, audit logs live in your PostgreSQL cluster. Query them directly with any BI or SIEM tool that supports PostgreSQL — no export pipeline required.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the audit log does NOT record
&lt;/h2&gt;

&lt;p&gt;Knowing the boundaries of any control is as important as knowing what it covers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secret values are never logged.&lt;/strong&gt; &lt;code&gt;SECRET_RUNTIME_ACCESSED&lt;/code&gt; records that a secret named &lt;code&gt;DATABASE_URL&lt;/code&gt; was accessed for deployment &lt;code&gt;api-prod&lt;/code&gt;. It does not record the value of &lt;code&gt;DATABASE_URL&lt;/code&gt;. The plaintext never touches the audit log.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application-level data is not recorded.&lt;/strong&gt; NEXUS AI audits actions taken on the platform — deployments, secrets, members, tokens. It does not audit what your application does with the resources it receives. If your app logs &lt;code&gt;process.env.DATABASE_URL&lt;/code&gt; at startup, that is an application-level concern, not a platform-level audit event.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read operations on deployments are not individually logged.&lt;/strong&gt; Viewing a deployment's status in the dashboard does not produce an audit event. Audit events capture state changes and security-relevant reads (secrets, databases). Routine dashboard reads would generate millions of low-value INFO events per day on active organizations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Container stdout/stderr is not the audit log.&lt;/strong&gt; Build logs and runtime logs are separate from the audit log. They are accessible via &lt;code&gt;GET /api/deployments/:id/logs&lt;/code&gt; and stored in the observability layer, not in &lt;code&gt;audit_logs&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Checklist: audit log hygiene
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Review &lt;code&gt;GET /api/audit/security-summary&lt;/code&gt; weekly — if the score drops below 80, investigate&lt;/li&gt;
&lt;li&gt;[ ] Set &lt;code&gt;AUDIT_LOG_RETENTION_DAYS=365&lt;/code&gt; for HIPAA, financial, or PCI workloads (Enterprise plan)&lt;/li&gt;
&lt;li&gt;[ ] Export monthly CSVs to your compliance archive before the quarterly close&lt;/li&gt;
&lt;li&gt;[ ] Pull &lt;code&gt;GET /api/audit/user-activity/:userId&lt;/code&gt; for every departing team member as part of offboarding&lt;/li&gt;
&lt;li&gt;[ ] Filter for &lt;code&gt;PERMISSION_CHANGED&lt;/code&gt; events during quarterly access reviews — verify every role change was intentional&lt;/li&gt;
&lt;li&gt;[ ] Confirm no &lt;code&gt;SECRET_REVEALED&lt;/code&gt; events in the last 30 days unless explicitly authorized&lt;/li&gt;
&lt;li&gt;[ ] Set up a SIEM pipeline for &lt;code&gt;severity=WARNING&lt;/code&gt; events if your team size exceeds 10 engineers&lt;/li&gt;
&lt;li&gt;[ ] On Enterprise On-Prem: verify your PostgreSQL backup schedule includes the &lt;code&gt;audit_logs&lt;/code&gt; table&lt;/li&gt;
&lt;li&gt;[ ] Document your retention period in your security policy before your next audit&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Can I delete an audit log entry?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No. Audit log records are append-only. There is no API endpoint to delete individual records. The retention job purges records older than the retention window — but only non-CRITICAL events, and only automatically. This immutability is intentional: an audit log you can edit is not an audit log.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who can access audit logs?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Audit log access requires the &lt;code&gt;audit.read&lt;/code&gt; org permission, which is granted to OWNER and ADMIN roles. Developer and lower roles cannot query the audit log. This prevents a Developer from inspecting what other users have done — audit visibility is a privilege, not a default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does the audit log capture API calls made by NEXUS AI MCP tools (Claude agents)?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Yes. MCP tool calls go through the same API layer as CLI and dashboard actions. They produce audit events with the token ID in &lt;code&gt;details.tokenId&lt;/code&gt; and a &lt;code&gt;userAgent&lt;/code&gt; that identifies the MCP client. You can filter for agent-originated actions by querying for the specific token ID issued to your MCP integration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens to audit logs if I downgrade my plan?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Logs are not deleted on plan downgrade. If you downgrade from Enterprise (365-day retention) to Pro (90-day retention), the retention job will begin purging records older than 90 days on its next daily run. Export before downgrading if you need records beyond the 90-day window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is the audit log encrypted at rest?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Yes. The &lt;code&gt;audit_logs&lt;/code&gt; table lives in the same PostgreSQL database as the rest of your organization's data. The database is encrypted at rest using AES-256. On Enterprise On-Prem, encryption at rest is your infrastructure team's responsibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I receive real-time alerts for specific event types?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
CRITICAL events (SUSPICIOUS_ACTIVITY, CONTAINER_ESCAPE_ATTEMPT) trigger the alert pipeline immediately. For custom alerting on other event types — for example, an alert any time &lt;code&gt;PERMISSION_CHANGED&lt;/code&gt; fires — the current path is to poll &lt;code&gt;/api/audit/logs&lt;/code&gt; via your SIEM and configure alert rules there. Native webhook delivery for specific event types is on the product roadmap.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;The audit log is available on every NEXUS AI plan, including Starter at $29/mo. The 90-day retention window, CSV export, and full API access ship on all plans. Configurable retention windows (&lt;code&gt;AUDIT_LOG_RETENTION_DAYS&lt;/code&gt;) and indefinite CRITICAL event retention are available on Enterprise and Enterprise On-Prem.&lt;/p&gt;

&lt;p&gt;If you are working through a HIPAA Business Associate Agreement, SOC 2 audit, or PCI self-assessment questionnaire, the compliance table in this post maps directly to the evidence your auditor needs. The CSV export at &lt;code&gt;/api/audit/export&lt;/code&gt; is the artifact.&lt;/p&gt;

&lt;p&gt;For regulated workloads, healthcare data, or financial applications, the Enterprise plan adds configurable retention, dedicated security review, and SAML SSO. Reach out at &lt;a href="https://nexusai.run" rel="noopener noreferrer"&gt;nexusai.run&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Related reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stop shipping secrets. Start using a vault.&lt;/li&gt;
&lt;li&gt;RBAC deep dive: roles, scopes, and least privilege&lt;/li&gt;
&lt;li&gt;MCP integration: 37 tools for Claude and AI agents&lt;/li&gt;
&lt;li&gt;How NEXUS AI deploys your app in under 5 minutes&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;What you can't see, you can't defend. What you can't prove, you can't audit.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>web</category>
      <category>programming</category>
    </item>
    <item>
      <title>NEXUS AI GitHub Integration: The Complete Guide to Auto-Deploying from Your Repository</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Sun, 09 Aug 2026 13:27:38 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/nexus-ai-github-integration-the-complete-guide-to-auto-deploying-from-your-repository-11e</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/nexus-ai-github-integration-the-complete-guide-to-auto-deploying-from-your-repository-11e</guid>
      <description>&lt;h1&gt;
  
  
  NEXUS AI GitHub Integration: The Complete Guide to Auto-Deploying from Your Repository
&lt;/h1&gt;

&lt;p&gt;Connecting your GitHub repository to NEXUS AI unlocks a fully automated deployment pipeline — push code, and your app is live within minutes on GCP Cloud Run, AWS ECS Fargate, Azure Container Apps, or NEXUS AI Container. This guide walks through every step: installing the GitHub App, binding a repo, configuring auto-deploy rules, and understanding the webhook system that powers it all.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Integrate GitHub with NEXUS AI?
&lt;/h2&gt;

&lt;p&gt;Modern software teams live in GitHub. Your pull requests, code reviews, branch strategies, and release tags all live there — so your deployment pipeline should follow that same source of truth.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;NEXUS AI GitHub integration&lt;/strong&gt; makes your repository the single trigger for deployments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No manual image builds or &lt;code&gt;docker push&lt;/code&gt; commands&lt;/li&gt;
&lt;li&gt;No CI pipeline boilerplate to maintain&lt;/li&gt;
&lt;li&gt;Branch-level control over which pushes trigger a deploy&lt;/li&gt;
&lt;li&gt;Encrypted environment variables stored securely at the binding level&lt;/li&gt;
&lt;li&gt;Full webhook audit trail — see every event NEXUS AI received from GitHub&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Install the NEXUS AI GitHub App
&lt;/h2&gt;

&lt;p&gt;To &lt;strong&gt;connect GitHub to NEXUS AI&lt;/strong&gt;, you need to install the NEXUS AI GitHub App on your GitHub account or organisation.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to your NEXUS AI dashboard at &lt;a href="https://nexusai.run" rel="noopener noreferrer"&gt;nexusai.run&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Integrations → GitHub&lt;/strong&gt; (or open any project and click &lt;strong&gt;Connect GitHub&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Connect GitHub&lt;/strong&gt; button. You will be redirected to GitHub's App installation page.&lt;/li&gt;
&lt;li&gt;Choose whether to install on your personal account or a GitHub organisation.&lt;/li&gt;
&lt;li&gt;Select the repositories you want to grant access to (you can choose &lt;strong&gt;All repositories&lt;/strong&gt; or restrict to specific ones).&lt;/li&gt;
&lt;li&gt;Confirm the installation. GitHub will redirect you back to NEXUS AI with your installation ID automatically captured.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Required GitHub App Permissions
&lt;/h3&gt;

&lt;p&gt;The NEXUS AI GitHub App requests the minimum permissions needed to operate:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Permission&lt;/th&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Repository contents&lt;/td&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;td&gt;Clone source code to build containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Webhook events&lt;/td&gt;
&lt;td&gt;Push&lt;/td&gt;
&lt;td&gt;Receive push notifications to trigger deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;No write access to your repository is ever requested. NEXUS AI only reads code and listens for push events.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Bind a Repository to a Deployment Target
&lt;/h2&gt;

&lt;p&gt;Once the app is installed, you need to &lt;strong&gt;bind a GitHub repo&lt;/strong&gt; to a deployment target in NEXUS AI. A binding links one branch (or set of branches) to one runtime environment with its own build configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Repo Binding
&lt;/h3&gt;

&lt;p&gt;In the NEXUS AI dashboard:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Deployments → New Deployment&lt;/strong&gt; or open an existing project.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;GitHub&lt;/strong&gt; as the source.&lt;/li&gt;
&lt;li&gt;Choose your installed GitHub account/org and pick a repository from the dropdown.&lt;/li&gt;
&lt;li&gt;Configure the binding options below.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Repo Binding Configuration Options
&lt;/h3&gt;

&lt;p&gt;Each binding exposes the following fields:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Branch control&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;allowedBranches&lt;/code&gt; — JSON array of branch names allowed to trigger deployments. Example: &lt;code&gt;["main", "production"]&lt;/code&gt;. Pushes to any other branch are silently ignored.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Deployment behaviour&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;autoDeploy&lt;/code&gt; — Boolean. When &lt;code&gt;true&lt;/code&gt;, every push to an allowed branch queues a deployment automatically. When &lt;code&gt;false&lt;/code&gt;, you must trigger deploys manually.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;projectId&lt;/code&gt; — Associate this binding with a NEXUS AI project for grouping and access control.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Build configuration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;buildMode&lt;/code&gt; — Build strategy. NEXUS AI auto-detects the language and framework, or you can override it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;installCommand&lt;/code&gt; — Dependency installation step. Example: &lt;code&gt;npm ci&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;buildCommand&lt;/code&gt; — Build step. Example: &lt;code&gt;npm run build&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;startCommand&lt;/code&gt; — Container entrypoint. Example: &lt;code&gt;node server.js&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;outputDir&lt;/code&gt; — Build output directory (used for static sites and SSR frameworks).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;servicePort&lt;/code&gt; — The port your application listens on inside the container. Example: &lt;code&gt;3000&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Runtime target&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;runtimeTarget&lt;/code&gt; — Where to deploy the container. Options:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gcp_cloud_run&lt;/code&gt; — Google Cloud Run (serverless containers)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws_ecs_fargate&lt;/code&gt; — AWS ECS with Fargate (serverless containers)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;azure_container_apps&lt;/code&gt; — Azure Container Apps&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Container&lt;/code&gt; — NEXUS AI&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Serving&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;subdomain&lt;/code&gt; — NEXUS AI-managed subdomain (e.g. &lt;code&gt;myapp.nexusai.run&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;customDomain&lt;/code&gt; — Your own domain (e.g. &lt;code&gt;app.example.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;envVars&lt;/code&gt; — Environment variables stored encrypted at rest. Never visible in logs or UI after saving.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example: Node.js App to GCP Cloud Run
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allowedBranches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"autoDeploy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"runtimeTarget"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gcp_cloud_run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"installCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm ci"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"buildCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"startCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node server.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"servicePort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subdomain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myapp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"envVars"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"NODE_ENV"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"DATABASE_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"postgresql://..."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Enable Auto-Deploy from GitHub
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Auto-deploying from GitHub&lt;/strong&gt; is controlled by two settings working together: &lt;code&gt;autoDeploy&lt;/code&gt; and &lt;code&gt;allowedBranches&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;autoDeploy: true&lt;/code&gt; and &lt;code&gt;allowedBranches: ["main"]&lt;/code&gt;, the full deployment flow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You run &lt;code&gt;git push origin main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;GitHub sends a &lt;code&gt;push&lt;/code&gt; event webhook to &lt;code&gt;https://nexusai.run/api/github/webhook&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;NEXUS AI verifies the HMAC-SHA256 signature on the &lt;code&gt;x-hub-signature-256&lt;/code&gt; header to confirm the event is genuine&lt;/li&gt;
&lt;li&gt;NEXUS AI checks if the pushed branch (&lt;code&gt;main&lt;/code&gt;) is in your &lt;code&gt;allowedBranches&lt;/code&gt; list&lt;/li&gt;
&lt;li&gt;NEXUS AI checks that &lt;code&gt;autoDeploy&lt;/code&gt; is enabled on the binding&lt;/li&gt;
&lt;li&gt;A deployment job is queued and picked up by a worker (up to 5 concurrent builds per worker, polling every 2 seconds)&lt;/li&gt;
&lt;li&gt;Your container is built from source and pushed to the target runtime&lt;/li&gt;
&lt;li&gt;Status progresses through: &lt;code&gt;queued → building → deploying → success&lt;/code&gt; (or &lt;code&gt;failed&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Duplicate Commit Protection
&lt;/h3&gt;

&lt;p&gt;NEXUS AI deduplicates deployments by commit SHA. If you push the same commit twice (e.g. via a force-push that does not change the tree), NEXUS AI will not re-deploy it. Additionally, if a newer commit arrives while an older one is still queued, the older job is superseded — you always deploy the latest code, never a stale intermediate commit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Manual Deployments
&lt;/h2&gt;

&lt;p&gt;You do not have to rely on push webhooks. To trigger a deployment on demand:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Via the dashboard:&lt;/strong&gt; Open the binding and click the &lt;strong&gt;Deploy&lt;/strong&gt; button. NEXUS AI fetches the latest commit on the configured branch and queues a build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Via the REST API:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://nexusai.run/api/github/bindings/:bindingId/deploy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;your-api-token&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;:bindingId&lt;/code&gt; with the UUID of your repo binding, visible in the binding detail page URL.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Understanding the Webhook System
&lt;/h2&gt;

&lt;p&gt;Every &lt;code&gt;push&lt;/code&gt; event GitHub sends to NEXUS AI is recorded in the &lt;strong&gt;Webhook Deliveries&lt;/strong&gt; tab of your binding. This gives you a full audit trail of what happened and why.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Can See
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Delivery ID&lt;/td&gt;
&lt;td&gt;GitHub's unique ID for the webhook delivery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event type&lt;/td&gt;
&lt;td&gt;Always &lt;code&gt;push&lt;/code&gt; for deployment triggers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Status&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ok&lt;/code&gt;, &lt;code&gt;ignored&lt;/code&gt;, or &lt;code&gt;failed&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reason&lt;/td&gt;
&lt;td&gt;Why the event was ignored (if applicable)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timestamp&lt;/td&gt;
&lt;td&gt;When the event was received&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Common "Ignored" Reasons
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;autoDeploy disabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The binding has &lt;code&gt;autoDeploy: false&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;branch not in allowedBranches&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The pushed branch is not in your allowed list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;installation suspended&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The GitHub App install was suspended by a GitHub admin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;duplicate commit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;This exact commit SHA was already deployed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The webhook endpoint is: &lt;strong&gt;&lt;code&gt;https://nexusai.run/api/github/webhook&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All payloads are verified using HMAC-SHA256 with a per-installation secret. Requests with an invalid or missing &lt;code&gt;x-hub-signature-256&lt;/code&gt; header are rejected immediately with no processing.&lt;/p&gt;




&lt;h2&gt;
  
  
  GitHub Actions Integration
&lt;/h2&gt;

&lt;p&gt;NEXUS AI does not replace GitHub Actions — it complements it. A common pattern is to run your test suite in GitHub Actions and only deploy when tests pass, using the NEXUS AI manual deploy API as the final step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/deploy.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test and Deploy&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;20"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;

  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Trigger NEXUS AI deployment&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;curl -X POST \&lt;/span&gt;
            &lt;span class="s"&gt;"https://nexusai.run/api/github/bindings/$NEXUS_BINDING_ID/deploy" \&lt;/span&gt;
            &lt;span class="s"&gt;-H "Authorization: Bearer $NEXUS_API_TOKEN" \&lt;/span&gt;
            &lt;span class="s"&gt;-H "Content-Type: application/json"&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;NEXUS_BINDING_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.NEXUS_BINDING_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;NEXUS_API_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.NEXUS_API_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;NEXUS_BINDING_ID&lt;/code&gt; and &lt;code&gt;NEXUS_API_TOKEN&lt;/code&gt; as GitHub Actions secrets in your repository settings (&lt;strong&gt;Settings → Secrets and variables → Actions&lt;/strong&gt;). With this pattern, set &lt;code&gt;autoDeploy: false&lt;/code&gt; on the NEXUS AI binding — deployments are controlled entirely by your Actions workflow, not raw push events.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Which GitHub plan do I need?&lt;/strong&gt;&lt;br&gt;
A: The NEXUS AI GitHub App works with free, Pro, Team, and Enterprise GitHub plans. It supports both personal accounts and organisations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can I bind the same repo to multiple deployment targets?&lt;/strong&gt;&lt;br&gt;
A: Yes. You can create multiple bindings for the same repository — for example, one binding deploying &lt;code&gt;main&lt;/code&gt; to production on GCP Cloud Run and another deploying &lt;code&gt;staging&lt;/code&gt; to a Docker host.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How do I rotate my webhook secret?&lt;/strong&gt;&lt;br&gt;
A: Reinstall or refresh the GitHub App connection from the NEXUS AI integrations page. A new installation ID and secret are generated automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Are environment variables secure?&lt;/strong&gt;&lt;br&gt;
A: Yes. All values stored in &lt;code&gt;envVars&lt;/code&gt; on a binding are encrypted at rest using AES-256-GCM. They are injected into the container at runtime and never logged or exposed in the dashboard after the initial save.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What happens if a deployment fails?&lt;/strong&gt;&lt;br&gt;
A: The deployment status is set to &lt;code&gt;failed&lt;/code&gt;. Your previous deployment continues running — NEXUS AI uses a rolling deployment strategy and never takes down a live container until a healthy replacement is confirmed. Build logs are available in the &lt;strong&gt;Deployments&lt;/strong&gt; tab and you can trigger a retry at any time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How do I disconnect GitHub?&lt;/strong&gt;&lt;br&gt;
A: You can suspend or uninstall the NEXUS AI GitHub App at any time from &lt;strong&gt;GitHub → Settings → Applications → Installed GitHub Apps&lt;/strong&gt;. In-flight deployments will complete; all future webhooks will be rejected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can I use GitHub integration with a monorepo?&lt;/strong&gt;&lt;br&gt;
A: Yes. Use the &lt;code&gt;buildCommand&lt;/code&gt; and &lt;code&gt;outputDir&lt;/code&gt; fields to scope the build to a specific package or subdirectory. For example: &lt;code&gt;"buildCommand": "cd packages/api &amp;amp;&amp;amp; npm run build"&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;NEXUS AI GitHub integration&lt;/strong&gt; connects your repository to a fully automated, cloud-native deployment pipeline in three steps: install the GitHub App, create a repo binding with your target runtime and build configuration, then enable &lt;code&gt;autoDeploy&lt;/code&gt;. Every push to an allowed branch is cryptographically verified, deduplicated by commit SHA, and deployed — with a full webhook audit trail so you always know exactly what happened and why.&lt;/p&gt;

&lt;p&gt;For questions or support, visit the &lt;a href="https://nexusai.run/docs" rel="noopener noreferrer"&gt;NEXUS AI documentation&lt;/a&gt; or open a support ticket from your dashboard.&lt;/p&gt;

</description>
      <category>github</category>
      <category>integrations</category>
      <category>cicd</category>
      <category>tutorials</category>
    </item>
    <item>
      <title>MCP-driven deploys: 5 tasks your Claude agent should be running for you</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Sun, 09 Aug 2026 13:25:45 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/mcp-driven-deploys-5-tasks-your-claude-agent-should-be-running-for-you-17ea</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/mcp-driven-deploys-5-tasks-your-claude-agent-should-be-running-for-you-17ea</guid>
      <description>&lt;h1&gt;
  
  
  MCP-driven deploys: 5 tasks your Claude agent should be running for you
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Published:&lt;/strong&gt; May 17, 2026&lt;br&gt;
&lt;strong&gt;Category:&lt;/strong&gt; AI · MCP · Operations&lt;br&gt;
&lt;strong&gt;Reading time:&lt;/strong&gt; 8 minutes&lt;br&gt;
&lt;strong&gt;Author:&lt;/strong&gt; NEXUS AI Team&lt;/p&gt;



&lt;p&gt;Most Model Context Protocol servers are read-only. They let your AI agent search a codebase, list GitHub issues, or look up Linear tickets. That is useful. It is also where most agents stop.&lt;/p&gt;

&lt;p&gt;NEXUS AI's MCP server is different. It exposes more than 50 write actions covering the full deployment lifecycle: build, deploy, scale, back up, restore, attach storage, query the database, fix schemas, roll back, and delete. Connect it to Claude Code, Cursor, Codex, or any MCP client, and your agent can run your infrastructure on your behalf.&lt;/p&gt;

&lt;p&gt;This post walks through five concrete tasks worth handing to your agent today.&lt;/p&gt;

&lt;p&gt;If your AI tools have only ever generated code for you, see also &lt;a href="https://nexusai.run/blog/your-ai-app-is-generated-now-how-do-you-deploy-it" rel="noopener noreferrer"&gt;Your AI app is generated. Now how do you deploy it?&lt;/a&gt; for the full picture of what changes when the agent owns operations.&lt;/p&gt;


&lt;h2&gt;
  
  
  Setup: connect the NEXUS AI MCP server in 60 seconds
&lt;/h2&gt;

&lt;p&gt;For Claude Desktop, add this to &lt;code&gt;~/Library/Application Support/Claude/claude_desktop_config.json&lt;/code&gt; (macOS) or &lt;code&gt;%APPDATA%\Claude\claude_desktop_config.json&lt;/code&gt; (Windows):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nexus-ai"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://nexusai.run/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer &amp;lt;your-nexus-token&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Cursor, add the same server in &lt;code&gt;Cursor Settings → MCP → Add server&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Generate a token at &lt;code&gt;https://nexusai.run/app/tokens&lt;/code&gt;. Scopes follow &lt;code&gt;deployments:read|create|delete&lt;/code&gt;, &lt;code&gt;db:query|admin&lt;/code&gt;, &lt;code&gt;secrets:read|manage&lt;/code&gt;, and similar boundaries. Start with read-only scopes if you want to watch the agent before giving it write access.&lt;/p&gt;

&lt;p&gt;Restart your AI client. The 50+ NEXUS AI tools appear in its tool list.&lt;/p&gt;




&lt;h2&gt;
  
  
  Task 1: Deploy from a prompt
&lt;/h2&gt;

&lt;p&gt;The classic case. You have a repository with working code. Tell the agent:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Deploy this repo to NEXUS AI. Use Postgres and Redis. Set the start command to &lt;code&gt;uvicorn app:app --host 0.0.0.0 --port 8000&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;nexusai_deploy_source&lt;/span&gt;
  &lt;span class="s"&gt;repo&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;           &lt;span class="s"&gt;https://github.com/you/my-app.git&lt;/span&gt;
  &lt;span class="s"&gt;framework&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;python&lt;/span&gt;
  &lt;span class="s"&gt;services&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;       &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;postgresql&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;start_command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uvicorn&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;app:app&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--host&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--port&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;8000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five minutes later the agent reports back with the live URL, the masked database connection string, and the deployment ID. You did not write a Dockerfile. You did not touch a dashboard.&lt;/p&gt;

&lt;p&gt;Why hand this over: the agent already has the repository context (it just wrote the code). It knows which framework, which port, which start command. The agent should know all of these without being told.&lt;/p&gt;




&lt;h2&gt;
  
  
  Task 2: Back up the database before a risky migration
&lt;/h2&gt;

&lt;p&gt;Your agent is about to run a schema migration. The right ops habit is "snapshot first, migrate second, validate third, restore on failure." That used to be three terminals, two Slack messages, and a prayer. Now it is one prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Before running the migration on &lt;code&gt;my-ai-app&lt;/code&gt;, take a Postgres backup. If the migration fails, restore from the backup and roll back the deploy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. nexusai_db_services_list                 → finds the postgres service id
2. nexusai_db_backup                        → triggers pg_dump, waits for SUCCESS
3. (runs your migration command)
4. nexusai_deploy_logs                      → reads runtime logs for errors
5a. (success) nexusai_db_backup_list        → confirms backup is on the retention list
5b. (failure) nexusai_db_restore            → restores from the backup
              nexusai_deploy_rollback       → rolls back to the previous release
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why hand this over: the failure path is the part humans forget to do under pressure. The agent always remembers.&lt;/p&gt;

&lt;p&gt;Backups land at &lt;code&gt;/var/lib/nexus-backups/&amp;lt;service&amp;gt;/&lt;/code&gt; and are downloadable through &lt;code&gt;nexusai_db_backup_download&lt;/code&gt;, which returns a signed URL with a 30 to 3600 second TTL.&lt;/p&gt;




&lt;h2&gt;
  
  
  Task 3: Diagnose a failing deploy from logs
&lt;/h2&gt;

&lt;p&gt;A deployment is stuck in &lt;code&gt;BUILDING&lt;/code&gt; or healthy but throwing 500s. Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is wrong with &lt;code&gt;my-ai-app&lt;/code&gt; right now? Tail the last 200 log lines, identify the failure, and propose a fix.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nexusai_deploy_status   → status, health, restart count, last error
nexusai_deploy_logs     → last 200 lines of build + runtime logs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It reads the actual stack trace, identifies the missing environment variable, the unhealthy upstream, or the failed migration, and replies with a targeted fix. If the fix is a code change, it edits the file in your IDE. If the fix is a secret, it calls &lt;code&gt;nexusai_secrets_create&lt;/code&gt;. If the fix is a redeploy, it calls &lt;code&gt;nexusai_deploy_redeploy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Why hand this over: incident triage is the part of operations that scales worst with team size. An agent in the loop never forgets to check the logs first.&lt;/p&gt;




&lt;h2&gt;
  
  
  Task 4: Query the database without leaving chat
&lt;/h2&gt;

&lt;p&gt;You want to know how many users signed up this week. You do not want to open &lt;code&gt;psql&lt;/code&gt;. Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How many users signed up in the last 7 days on &lt;code&gt;my-ai-app&lt;/code&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. nexusai_db_source_list           → resolves your database service
2. nexusai_db_inspect_schema        → confirms there is a `users` table with `created_at`
3. nexusai_db_query_preview         → returns the SQL it intends to run, you approve
4. nexusai_db_query_execute         → runs the read-only query, returns the count
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The preview step is the safety net. You see the SQL before it runs. For read-only queries the agent runs in a sandboxed session with a 5-second statement timeout and a row-count cap. For destructive queries the agent uses the AI-powered DDL fix workflow (&lt;code&gt;nexusai_db_propose_fix&lt;/code&gt; then &lt;code&gt;nexusai_db_apply_fix&lt;/code&gt;), which requires explicit approval.&lt;/p&gt;

&lt;p&gt;Why hand this over: most "look at the database" tasks are short, repetitive, and high-friction. Making them a chat message removes the friction.&lt;/p&gt;




&lt;h2&gt;
  
  
  Task 5: Roll back on incident
&lt;/h2&gt;

&lt;p&gt;A deploy went out at 3pm. Error rates spiked at 3:05. Tell the agent:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Roll back &lt;code&gt;my-ai-app&lt;/code&gt; to the previous release. Snapshot Postgres first in case we need to forward-fix.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. nexusai_db_backup           → snapshot before the rollback
2. nexusai_deploy_rollback     → revert to the previous container image
3. nexusai_deploy_status       → confirm RUNNING and healthy
4. nexusai_deploy_logs         → tail to verify error rate dropped
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rollback flips back to the previous image with the same database state. If the bug was schema-level you can restore from the backup separately.&lt;/p&gt;

&lt;p&gt;Why hand this over: rollbacks under stress are exactly when humans skip the snapshot step.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you should not hand over yet
&lt;/h2&gt;

&lt;p&gt;A few actions are deliberately gated. The agent can prepare them but cannot fire them without explicit confirmation:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Why gated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Delete a deployment (&lt;code&gt;nexusai_deploy_delete&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Permanent. Tears down containers, networks, volumes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Drop or truncate via &lt;code&gt;nexusai_db_query_execute&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Requires &lt;code&gt;db:admin&lt;/code&gt; scope and an explicit confirmation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete a bucket (&lt;code&gt;nexusai_bucket_delete&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Removes scoped service account and bucket contents.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove a volume (&lt;code&gt;nexusai_volume_delete&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Deletes the underlying data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rotate bucket credentials&lt;/td&gt;
&lt;td&gt;Invalidates current keys. Apps must redeploy.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The platform requires the agent to call a &lt;code&gt;_preview&lt;/code&gt; variant first (where available) or to set an explicit &lt;code&gt;confirm: true&lt;/code&gt; flag. The agent will pause and ask you before crossing one of these lines.&lt;/p&gt;




&lt;h2&gt;
  
  
  A minimal Claude system prompt for operations
&lt;/h2&gt;

&lt;p&gt;If you want Claude to behave as a careful operator rather than an enthusiastic deployer, paste this into your project's system instructions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are operating a NEXUS AI account via MCP tools. Rules:

1. Read before you write. Always call status/list/inspect tools before
   making changes.
2. Snapshot before destructive operations. Always call nexusai_db_backup
   before migrations or restores.
3. Show the SQL before running it. Always call nexusai_db_query_preview
   before nexusai_db_query_execute.
4. Confirm before deletes. Never call delete tools without an explicit
   "yes, delete &amp;lt;resource&amp;gt;" from the user in the same message.
5. Prefer rollback over fix-forward during an incident. Stabilize first,
   debug second.
6. Stream logs after every deploy or restart and summarize the first
   error you see.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Six lines turn a code-generating agent into an operations partner.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this changes about your workflow
&lt;/h2&gt;

&lt;p&gt;Once the agent can deploy and operate, three things shift:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operations cost drops.&lt;/strong&gt; Tasks that took 5 to 30 minutes (snapshot, deploy, tail logs, rollback) become one prompt. The agent does the boilerplate, you make the decision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Incident response gets faster.&lt;/strong&gt; The agent already has context (recent code changes, recent deploys, recent logs). It can triage in seconds instead of minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Experimentation gets cheaper.&lt;/strong&gt; Standing up a new app, attaching a database, testing an idea, tearing it down: each step is one prompt instead of one ticket.&lt;/p&gt;

&lt;p&gt;The agent is no longer a code-completion tool. It is the operator on call.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Which MCP clients work with NEXUS AI?&lt;/strong&gt;&lt;br&gt;
Claude Desktop, Claude Code, Cursor, Codex CLI, Windsurf, Zed AI, and any client that speaks MCP over HTTP or stdio. NEXUS AI publishes a hosted HTTPS endpoint, so any client that can attach a Bearer token works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does the agent need the same permissions I have?&lt;/strong&gt;&lt;br&gt;
No. NEXUS AI tokens are scoped. Start the agent with &lt;code&gt;deployments:read&lt;/code&gt;, &lt;code&gt;db:read&lt;/code&gt;, &lt;code&gt;logs:read&lt;/code&gt;. Promote scopes only when you want it to deploy or modify. Tokens are revocable from the dashboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can two agents operate the same account?&lt;/strong&gt;&lt;br&gt;
Yes. Each token is independent. Audit log entries record which token took each action, so you can attribute changes to "claude-prod", "cursor-staging", or any other label.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there a rate limit on MCP calls?&lt;/strong&gt;&lt;br&gt;
The platform applies standard API rate limits per token. Heavy reconciliation loops should batch (&lt;code&gt;*_list&lt;/code&gt; tools support pagination) instead of polling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does the agent see secret values?&lt;/strong&gt;&lt;br&gt;
No. &lt;code&gt;nexusai_secrets_list&lt;/code&gt; returns names and metadata. &lt;code&gt;nexusai_secrets_create&lt;/code&gt; accepts values you provide. The platform never returns decrypted secret values over the API or MCP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if the agent makes a mistake?&lt;/strong&gt;&lt;br&gt;
Every action is in the audit log (&lt;code&gt;AuditLog&lt;/code&gt; table, exportable from the dashboard). Most actions are reversible: stops have starts, deploys have rollbacks, backups have restores. Deletes are the only one-way door, and the agent will not call delete tools without explicit user confirmation in the same message.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://nexusai.run/register" rel="noopener noreferrer"&gt;Start free&lt;/a&gt; and connect the MCP server in your client of choice. Or ask your agent to do it for you.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>claude</category>
      <category>cursor</category>
      <category>agents</category>
    </item>
    <item>
      <title>Persistent storage in NEXUS AI</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Sun, 09 Aug 2026 13:24:13 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/persistent-storage-in-nexus-ai-2nbp</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/persistent-storage-in-nexus-ai-2nbp</guid>
      <description>&lt;h1&gt;
  
  
  Persistent storage in NEXUS AI: volumes and S3-compatible buckets for deployed apps
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Published:&lt;/strong&gt; May 9, 2026&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Storage · DevOps&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time:&lt;/strong&gt; 11 minutes&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Author:&lt;/strong&gt; NEXUS AI Team&lt;/p&gt;



&lt;p&gt;Most application deployment platforms make stateless services easy and stateful workloads awkward. That works until your app needs user uploads, generated reports, SQLite files, model artifacts, or a shared object store for background jobs.&lt;/p&gt;

&lt;p&gt;NEXUS AI now includes two storage primitives for deployed applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Volumes&lt;/strong&gt; for persistent filesystem mounts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Buckets&lt;/strong&gt; for S3-compatible object storage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are organization-scoped. Both can be attached to deployments. Both survive container restarts, redeploys, and deployment deletion until you explicitly delete the storage resource.&lt;/p&gt;

&lt;p&gt;This post explains when to use each one, how to attach them, how they behave during scaling, and the exact CLI commands to run.&lt;/p&gt;


&lt;h2&gt;
  
  
  Two storage primitives
&lt;/h2&gt;

&lt;p&gt;Volumes and buckets solve different problems.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Access pattern&lt;/th&gt;
&lt;th&gt;Attachment model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Volumes&lt;/td&gt;
&lt;td&gt;Filesystem state, SQLite, local uploads, persistent caches&lt;/td&gt;
&lt;td&gt;App reads and writes a mounted path like &lt;code&gt;/data&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Single deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Buckets&lt;/td&gt;
&lt;td&gt;User media, reports, generated assets, object storage&lt;/td&gt;
&lt;td&gt;App uses S3-compatible SDK calls&lt;/td&gt;
&lt;td&gt;Multiple deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use a &lt;strong&gt;volume&lt;/strong&gt; when your app expects a local filesystem path.&lt;/p&gt;

&lt;p&gt;Use a &lt;strong&gt;bucket&lt;/strong&gt; when your app can store data as objects by key using an S3 SDK.&lt;/p&gt;


&lt;h2&gt;
  
  
  Volumes: persistent filesystem mounts
&lt;/h2&gt;

&lt;p&gt;A NEXUS AI volume is a persistent filesystem mount backed by a Docker named volume. Your app writes to a path, usually &lt;code&gt;/data&lt;/code&gt;, and the data remains available after redeploys or container replacement.&lt;/p&gt;

&lt;p&gt;Create a volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus volume create app-data &lt;span class="nt"&gt;--display-name&lt;/span&gt; &lt;span class="s2"&gt;"App data"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy your app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/your-org/your-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; myapp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; docker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attach the volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus volume attach &amp;lt;volume-id&amp;gt; &amp;lt;deployment-id&amp;gt; &lt;span class="nt"&gt;--mount&lt;/span&gt; /data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Redeploy so the new container starts with the mount:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy redeploy &amp;lt;deployment-id&amp;gt; &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That redeploy step matters. Docker mounts are applied when a container is created. You cannot add a volume mount to an already-running container.&lt;/p&gt;

&lt;p&gt;Verify the mount:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &amp;lt;container-id&amp;gt; &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  When to use volumes
&lt;/h2&gt;

&lt;p&gt;Volumes are a good fit for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQLite databases for small apps.&lt;/li&gt;
&lt;li&gt;User uploads written through the filesystem.&lt;/li&gt;
&lt;li&gt;Persistent cache directories.&lt;/li&gt;
&lt;li&gt;Generated files that your app expects to read from disk.&lt;/li&gt;
&lt;li&gt;Model files or local indexes that survive redeploys.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Volumes are not a good fit for every scaled workload. If multiple app replicas write the same file at the same time, your application needs to handle locking or coordination.&lt;/p&gt;

&lt;p&gt;Think of a NEXUS AI volume like a shared network drive. It persists, but it does not magically make unsafe concurrent writes safe.&lt;/p&gt;




&lt;h2&gt;
  
  
  How volumes behave when you scale
&lt;/h2&gt;

&lt;p&gt;When you scale a deployment up, every replica mounts the same named volume at the same path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy scale &amp;lt;deployment-id&amp;gt; 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three replicas now see the same files under &lt;code&gt;/data&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That is useful for read-mostly data, shared assets, or workloads with explicit locking. It is risky for apps that assume a single writer, such as a default SQLite setup with multiple write-heavy replicas.&lt;/p&gt;

&lt;p&gt;When you scale down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy scale &amp;lt;deployment-id&amp;gt; 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The removed containers detach. The volume remains. The remaining replica keeps using the same data.&lt;/p&gt;

&lt;p&gt;When you delete the deployment, the volume still survives. You must explicitly detach and delete the volume if you want to destroy the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus volume detach &amp;lt;volume-id&amp;gt;
nexus volume delete &amp;lt;volume-id&amp;gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Buckets: S3-compatible object storage
&lt;/h2&gt;

&lt;p&gt;A NEXUS AI bucket is S3-compatible object storage backed by MinIO. Your app interacts with it using standard AWS SDKs, boto3, or any S3-compatible client.&lt;/p&gt;

&lt;p&gt;Create a bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus bucket create user-uploads &lt;span class="nt"&gt;--display-name&lt;/span&gt; &lt;span class="s2"&gt;"User uploads"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attach it to a deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus bucket attach &amp;lt;bucket-id&amp;gt; &amp;lt;deployment-id&amp;gt;
nexus deploy redeploy &amp;lt;deployment-id&amp;gt; &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After redeploy, NEXUS AI injects S3 environment variables into the app container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;S3_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://host.docker.internal:9000
&lt;span class="nv"&gt;S3_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-east-1
&lt;span class="nv"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;org-&amp;lt;orgIdShort&amp;gt;-&amp;lt;bucketName&amp;gt;
&lt;span class="nv"&gt;S3_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;scoped access key&amp;gt;
&lt;span class="nv"&gt;S3_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;scoped secret key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If multiple buckets are attached, NEXUS AI also injects per-bucket aliases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;S3_BUCKET_USER_UPLOADS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;org-...-user-uploads
&lt;span class="nv"&gt;S3_BUCKET_USER_UPLOADS_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...
&lt;span class="nv"&gt;S3_BUCKET_USER_UPLOADS_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Example: write files to a bucket from Python
&lt;/h2&gt;

&lt;p&gt;Your app can use boto3 with the injected environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;endpoint_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S3_ENDPOINT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S3_ACCESS_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S3_SECRET_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S3_REGION&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uploads/hello.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello from NEXUS AI&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That same pattern works with the AWS SDK for JavaScript, Go, Java, Ruby, PHP, and other S3-compatible clients.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bucket file operations from the CLI
&lt;/h2&gt;

&lt;p&gt;List files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus bucket files &amp;lt;bucket-id&amp;gt;
nexus bucket files &amp;lt;bucket-id&amp;gt; &lt;span class="nt"&gt;--prefix&lt;/span&gt; uploads/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upload a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus bucket upload &amp;lt;bucket-id&amp;gt; ./report.pdf &lt;span class="nt"&gt;--key&lt;/span&gt; reports/report.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus bucket download &amp;lt;bucket-id&amp;gt; reports/report.pdf &lt;span class="nt"&gt;--out&lt;/span&gt; ./report.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate a short-lived signed download URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus bucket download &amp;lt;bucket-id&amp;gt; reports/report.pdf &lt;span class="nt"&gt;--share&lt;/span&gt; &lt;span class="nt"&gt;--ttl&lt;/span&gt; 900
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Delete a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus bucket &lt;span class="nb"&gt;rm&lt;/span&gt; &amp;lt;bucket-id&amp;gt; reports/report.pdf &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI upload path streams from disk, which is better for large files than browser uploads.&lt;/p&gt;




&lt;h2&gt;
  
  
  Scoped credentials and rotation
&lt;/h2&gt;

&lt;p&gt;Each bucket gets scoped S3 credentials. A deployment attached to one bucket does not automatically get access to every bucket in the organization.&lt;/p&gt;

&lt;p&gt;Reveal credentials for external clients:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus bucket credentials &amp;lt;bucket-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rotate credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus bucket rotate-credentials &amp;lt;bucket-id&amp;gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;
nexus deploy redeploy &amp;lt;deployment-id&amp;gt; &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Redeploy is required because the running container already has the old environment variables. The next container start receives the new &lt;code&gt;S3_ACCESS_KEY&lt;/code&gt; and &lt;code&gt;S3_SECRET_KEY&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Buckets and scaling
&lt;/h2&gt;

&lt;p&gt;Buckets are usually a better fit for scaled apps than shared filesystem volumes.&lt;/p&gt;

&lt;p&gt;When you scale up, every replica receives the same S3 environment variables. Each replica makes independent S3 API calls. The object storage layer handles concurrent requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy scale &amp;lt;deployment-id&amp;gt; 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For object keys, the normal S3 rule applies: if two replicas write the same key, the last write wins. Use unique keys when each replica should produce independent output.&lt;/p&gt;

&lt;p&gt;When you scale down, nothing needs to detach. The removed container stops making S3 calls. The bucket and objects remain.&lt;/p&gt;




&lt;h2&gt;
  
  
  REST API reference
&lt;/h2&gt;

&lt;p&gt;Volumes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET    /api/volumes
POST   /api/volumes
POST   /api/volumes/:id/attach
POST   /api/volumes/:id/detach
POST   /api/volumes/:id/refresh-usage
DELETE /api/volumes/:id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Buckets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET    /api/buckets
POST   /api/buckets
POST   /api/buckets/:id/attach
POST   /api/buckets/:id/detach
POST   /api/buckets/:id/refresh-usage
POST   /api/buckets/:id/rotate-credentials
GET    /api/buckets/:id/credentials
GET    /api/buckets/:id/files
PUT    /api/buckets/:id/files/:key
GET    /api/buckets/:id/files/:key/download
POST   /api/buckets/:id/files/:key/download-url
DELETE /api/buckets/:id/files/:key
GET    /api/bucket-downloads/:token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example volume attach request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_BASE&lt;/span&gt;&lt;span class="s2"&gt;/volumes/&amp;lt;volume-id&amp;gt;/attach"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_JWT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"deploymentId":"&amp;lt;deployment-id&amp;gt;","mountPath":"/data"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example bucket attach request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_BASE&lt;/span&gt;&lt;span class="s2"&gt;/buckets/&amp;lt;bucket-id&amp;gt;/attach"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_JWT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"deploymentId":"&amp;lt;deployment-id&amp;gt;"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  MCP tools
&lt;/h2&gt;

&lt;p&gt;AI clients can operate storage through MCP tools with scoped permissions.&lt;/p&gt;

&lt;p&gt;Volume tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nexusai_volume_list
nexusai_volume_create
nexusai_volume_attach
nexusai_volume_detach
nexusai_volume_delete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bucket tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nexusai_bucket_list
nexusai_bucket_create
nexusai_bucket_attach
nexusai_bucket_detach
nexusai_bucket_rotate_credentials
nexusai_bucket_files_list
nexusai_bucket_file_download
nexusai_bucket_file_delete
nexusai_bucket_delete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use MCP for operational flows like “create a bucket for this deployment, attach it, then remind me to redeploy,” while keeping destructive operations behind confirmation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Which one should you choose?
&lt;/h2&gt;

&lt;p&gt;Choose a &lt;strong&gt;volume&lt;/strong&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your app requires a filesystem path.&lt;/li&gt;
&lt;li&gt;You need persistent local files.&lt;/li&gt;
&lt;li&gt;You are running a single replica or have safe file-locking behavior.&lt;/li&gt;
&lt;li&gt;You want simple persistence for Docker-based deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choose a &lt;strong&gt;bucket&lt;/strong&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your app stores uploads or generated files.&lt;/li&gt;
&lt;li&gt;Multiple deployments need access to the same storage.&lt;/li&gt;
&lt;li&gt;The app may scale to multiple replicas.&lt;/li&gt;
&lt;li&gt;You want S3-compatible tooling and signed URLs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most user-upload and generated-asset workflows, buckets are the better long-term default. For apps that truly expect local disk, volumes are the practical answer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;NEXUS AI now gives deployed apps durable storage without forcing every app into the same model.&lt;/p&gt;

&lt;p&gt;Volumes give you persistent filesystem mounts. Buckets give you S3-compatible object storage with scoped credentials, file operations, signed URLs, and multi-deployment attachment.&lt;/p&gt;

&lt;p&gt;The rule is simple: use volumes for local filesystem state, and use buckets for object storage. Then redeploy after attaching so your containers receive the mount or environment variables they need.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>s3</category>
      <category>nexusai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Deploy a full-stack Python app with Postgres, Redis, and workers in 5 minutes</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Sun, 09 Aug 2026 13:21:53 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/deploy-a-full-stack-python-app-with-postgres-redis-and-workers-in-5-minutes-je1</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/deploy-a-full-stack-python-app-with-postgres-redis-and-workers-in-5-minutes-je1</guid>
      <description>&lt;h2&gt;
  
  
  Deploy a full-stack Python app with Postgres, Redis, and workers in 5 minutes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Published:&lt;/strong&gt; May 15, 2026&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Python · DevOps · Databases&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time:&lt;/strong&gt; 8 minutes&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Author:&lt;/strong&gt; NEXUS AI Team&lt;/p&gt;

&lt;p&gt;Most Python apps are not just one web process.&lt;/p&gt;

&lt;p&gt;Even a small production app usually needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A web API, often FastAPI, Flask, or Django.&lt;/li&gt;
&lt;li&gt;PostgreSQL for durable application data.&lt;/li&gt;
&lt;li&gt;Redis for queues, caching, rate limits, or sessions.&lt;/li&gt;
&lt;li&gt;A background worker for slow jobs like emails, scraping, AI calls, imports, PDF generation, or webhook processing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The painful part is usually not the code. It is wiring the app container, database, cache, worker process, service networking, ports, health checks, environment variables, volumes, logs, and scaling rules.&lt;/p&gt;

&lt;p&gt;With the NEXUS AI CLI, you can deploy the whole stack from a Git repository with one command.&lt;/p&gt;

&lt;p&gt;This post walks through a practical Python deployment using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python web app&lt;/li&gt;
&lt;li&gt;PostgreSQL service&lt;/li&gt;
&lt;li&gt;Redis service&lt;/li&gt;
&lt;li&gt;RQ background worker&lt;/li&gt;
&lt;li&gt;Internal service networking&lt;/li&gt;
&lt;li&gt;Scaling&lt;/li&gt;
&lt;li&gt;Logs&lt;/li&gt;
&lt;li&gt;Backup&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What we are deploying
&lt;/h2&gt;

&lt;p&gt;The target architecture looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
  |
  v
NEXUS AI 
  |
  v
Python web container
  |-- connects to postgresql:5432
  |-- connects to redis:6379
  |
  v
Worker container
  |-- runs rq worker default
  |-- connects to the same Postgres and Redis services

PostgreSQL container
Redis container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important detail: the app and worker do not connect to &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Inside the deployment network, the database hostnames are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;postgresql:5432
redis:6379
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NEXUS AI injects the environment variables your app needs, including &lt;code&gt;DATABASE_URL&lt;/code&gt; and &lt;code&gt;REDIS_URL&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A NEXUS AI account.&lt;/li&gt;
&lt;li&gt;The NEXUS CLI installed.&lt;/li&gt;
&lt;li&gt;A Git repository containing your Python app.&lt;/li&gt;
&lt;li&gt;A Python app that listens on a known port, usually &lt;code&gt;8000&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install the CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://nexusai.run/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On macOS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://nexusai.run/install-mac.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Authenticate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus auth login
nexus auth status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example Python app structure
&lt;/h2&gt;

&lt;p&gt;Your repository can be simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-python-app/
  app.py
  worker.py
  requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example &lt;code&gt;requirements.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fastapi
uvicorn[standard]
psycopg[binary]
redis
rq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example &lt;code&gt;app.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Redis&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rq&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Queue&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;redis_conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REDIS_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;redis_conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;processed &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/healthz&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;healthz&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/jobs/{name}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;enqueue_job&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;job_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;queued&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example &lt;code&gt;worker.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Redis&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rq&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Queue&lt;/span&gt;

&lt;span class="n"&gt;redis_conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REDIS_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;redis_conn&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;redis_conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a real app, your task functions usually live in a separate module so both the web process and worker can import them cleanly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy the full stack
&lt;/h2&gt;

&lt;p&gt;Run one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/your-org/my-python-app.git &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-python-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; docker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--framework&lt;/span&gt; python &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--branch&lt;/span&gt; main &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--services&lt;/span&gt; postgresql,redis &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--start-command&lt;/span&gt; &lt;span class="s2"&gt;"uvicorn app:app --host 0.0.0.0 --port 8000"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--worker-command&lt;/span&gt; &lt;span class="s2"&gt;"python worker.py"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--worker-name&lt;/span&gt; jobs-worker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That command does the operational work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pulls your Git repository.&lt;/li&gt;
&lt;li&gt;Detects/builds the Python app image.&lt;/li&gt;
&lt;li&gt;Creates the web app container.&lt;/li&gt;
&lt;li&gt;Creates a PostgreSQL service.&lt;/li&gt;
&lt;li&gt;Creates a Redis service.&lt;/li&gt;
&lt;li&gt;Creates a worker container from the same app image.&lt;/li&gt;
&lt;li&gt;Attaches the app, worker, Postgres, and Redis to the same service network.&lt;/li&gt;
&lt;li&gt;Injects database and Redis environment variables.&lt;/li&gt;
&lt;li&gt;Exposes only the web app publicly.&lt;/li&gt;
&lt;li&gt;Keeps the worker private.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The worker does not need a public port. It runs inside the same deployment network and talks to Redis/Postgres by internal hostname.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment variables your app receives
&lt;/h2&gt;

&lt;p&gt;For PostgreSQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POSTGRES_HOST=postgresql
POSTGRES_PORT=5432
POSTGRES_DB=appdb
POSTGRES_USER=appuser
POSTGRES_PASSWORD=&amp;lt;generated&amp;gt;
DATABASE_URL=postgresql://appuser:&amp;lt;generated&amp;gt;@postgresql:5432/appdb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Redis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REDIS_HOST=redis
REDIS_PORT=6379
REDIS_URL=redis://redis:6379/0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use these values inside the app and worker.&lt;/p&gt;

&lt;p&gt;Do not hardcode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;localhost
127.0.0.1
host.docker.internal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those point to the wrong place from inside a container. Use &lt;code&gt;postgresql&lt;/code&gt; and &lt;code&gt;redis&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check deployment status
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy status my-python-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the deployment move through states such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PENDING
BUILDING
RUNNING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When it is running, open the deployment URL and check the health route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://your-app-url/healthz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Watch app and worker logs
&lt;/h2&gt;

&lt;p&gt;Tail logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy logs my-python-app &lt;span class="nt"&gt;--follow&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your worker is connected correctly, you should see it listening on the queue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Listening on default...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the worker cannot resolve Redis, you will see errors like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error -2 connecting to redis:6379. Name or service not known.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That usually means the worker was not attached to the deployment network or the app is using the wrong Redis hostname.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;REDIS_URL=redis://redis:6379/0&lt;/code&gt;, not &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test the queue
&lt;/h2&gt;

&lt;p&gt;Send a job to the web app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://your-app-url/jobs/demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"job_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"queued"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then check logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy logs my-python-app &lt;span class="nt"&gt;--follow&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the worker pick up the job.&lt;/p&gt;




&lt;h2&gt;
  
  
  Scale the web app
&lt;/h2&gt;

&lt;p&gt;Scale the web containers to two replicas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy scale my-python-app 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scale to three:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy scale my-python-app 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scale back down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy scale my-python-app 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scaling changes the web app replicas. PostgreSQL and Redis are not duplicated. They remain the shared backing services for the deployment.&lt;/p&gt;

&lt;p&gt;This matters because a scaled Python app should be stateless at the web layer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store durable data in Postgres.&lt;/li&gt;
&lt;li&gt;Store queue/cache/session data in Redis.&lt;/li&gt;
&lt;li&gt;Store uploaded files in a bucket or attached volume.&lt;/li&gt;
&lt;li&gt;Do not rely on local container files unless you intentionally attached persistent storage.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Add production environment variables
&lt;/h2&gt;

&lt;p&gt;Pass runtime variables directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/your-org/my-python-app.git &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-python-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; docker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--framework&lt;/span&gt; python &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--services&lt;/span&gt; postgresql,redis &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;APP_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;QUEUE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;default &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--worker-command&lt;/span&gt; &lt;span class="s2"&gt;"python worker.py"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or load them from a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/your-org/my-python-app.git &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-python-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; docker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--framework&lt;/span&gt; python &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--services&lt;/span&gt; postgresql,redis &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt; .env.production &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--worker-command&lt;/span&gt; &lt;span class="s2"&gt;"python worker.py"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For sensitive values, use the secrets vault instead of committing &lt;code&gt;.env&lt;/code&gt; files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus secret create OPENAI_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then reference secrets during deployment or from the dashboard, depending on your workflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Back up Postgres
&lt;/h2&gt;

&lt;p&gt;List database services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db services my-python-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a backup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup &amp;lt;postgres-service-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List backups:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backups &amp;lt;postgres-service-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download a backup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-download &amp;lt;postgres-service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--out&lt;/span&gt; ./postgres.dump
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restore into the same service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db restore &amp;lt;postgres-service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restore into another deployment's Postgres service in the same organization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db restore-to &amp;lt;target-postgres-service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before restoring production data, pause write-heavy workers or scale them down if your workflow supports it. A restore can replace database state.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common fixes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The app cannot connect to Postgres
&lt;/h3&gt;

&lt;p&gt;Check that the app is using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL=postgresql://appuser:&amp;lt;password&amp;gt;@postgresql:5432/appdb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hostname must be &lt;code&gt;postgresql&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The worker cannot connect to Redis
&lt;/h3&gt;

&lt;p&gt;Check that the worker is using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REDIS_URL=redis://redis:6379/0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hostname must be &lt;code&gt;redis&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The worker starts, then exits
&lt;/h3&gt;

&lt;p&gt;Make sure the worker command is a long-running process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--worker-command&lt;/span&gt; &lt;span class="s2"&gt;"python worker.py"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--worker-command&lt;/span&gt; &lt;span class="s2"&gt;"rq worker default"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not use a one-shot command unless you intentionally want a short task.&lt;/p&gt;

&lt;h3&gt;
  
  
  The app deploys, but health checks fail
&lt;/h3&gt;

&lt;p&gt;Confirm the app listens on &lt;code&gt;0.0.0.0&lt;/code&gt;, not &lt;code&gt;127.0.0.1&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvicorn app:app &lt;span class="nt"&gt;--host&lt;/span&gt; 0.0.0.0 &lt;span class="nt"&gt;--port&lt;/span&gt; 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also make sure the app exposes a health endpoint such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/healthz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scaling up works, but file uploads disappear
&lt;/h3&gt;

&lt;p&gt;Local container files are ephemeral. Use a NEXUS AI bucket for user uploads or a persistent volume for filesystem-backed data.&lt;/p&gt;

&lt;p&gt;For object storage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus bucket create user-uploads &lt;span class="nt"&gt;--display-name&lt;/span&gt; &lt;span class="s2"&gt;"User uploads"&lt;/span&gt;
nexus bucket attach &amp;lt;bucket-id&amp;gt; &amp;lt;deployment-id&amp;gt;
nexus deploy redeploy &amp;lt;deployment-id&amp;gt; &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The 5-minute path
&lt;/h2&gt;

&lt;p&gt;If your app already has a working Python web process and worker command, the full deployment is one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/your-org/my-python-app.git &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-python-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; docker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--framework&lt;/span&gt; python &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--services&lt;/span&gt; postgresql,redis &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--start-command&lt;/span&gt; &lt;span class="s2"&gt;"uvicorn app:app --host 0.0.0.0 --port 8000"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--worker-command&lt;/span&gt; &lt;span class="s2"&gt;"python worker.py"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--worker-name&lt;/span&gt; jobs-worker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy status my-python-app
nexus deploy logs my-python-app &lt;span class="nt"&gt;--follow&lt;/span&gt;
curl https://your-app-url/healthz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the core workflow: deploy the web app, provision Postgres and Redis, attach the worker to the same network, and manage the whole stack from the CLI.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Can I use Celery instead of RQ?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Yes. Use a Celery worker command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--worker-command&lt;/span&gt; &lt;span class="s2"&gt;"celery -A app.celery worker --loglevel=info"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Does the worker get the same environment variables as the app?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Yes. The worker uses the same built image and receives the same deployment environment variables, including database and Redis connection values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does scaling duplicate Postgres or Redis?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No. Scaling changes app replicas. Database and cache services remain shared deployment resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should the worker expose a port?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No. Workers should stay private. They consume jobs from Redis and do not need public ingress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I deploy Django with this pattern?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Yes. Use a Django web command such as &lt;code&gt;gunicorn config.wsgi:application --bind 0.0.0.0:8000&lt;/code&gt; and a worker command such as &lt;code&gt;celery -A config worker --loglevel=info&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I use this with private GitHub repositories?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Yes. Store your repository token as a secret and deploy with &lt;code&gt;--repo-secret&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/your-org/private-python-app.git &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo-secret&lt;/span&gt; GITHUB_TOKEN &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; private-python-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; docker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--framework&lt;/span&gt; python &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--services&lt;/span&gt; postgresql,redis &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--worker-command&lt;/span&gt; &lt;span class="s2"&gt;"python worker.py"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ai</category>
      <category>python</category>
      <category>redis</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Replace Github Actions With One Command</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Sun, 09 Aug 2026 13:18:49 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/replace-github-actions-with-one-command-3p1o</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/replace-github-actions-with-one-command-3p1o</guid>
      <description>&lt;p&gt;My GitHub Actions deploy workflow was 87 lines of YAML.&lt;/p&gt;

&lt;p&gt;It had grown over 18 months from a clean 20-line file into something I was genuinely afraid to touch. It broke whenever a dependency updated. It had three hardcoded ARNs from an AWS account I was no longer using. It had a comment that said &lt;code&gt;# TODO: fix this&lt;/code&gt; that had been there for 11 months.&lt;/p&gt;

&lt;p&gt;Last month I deleted all 87 lines and replaced them with one command.&lt;/p&gt;

&lt;p&gt;Here's exactly how I did it — and what I learned along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The YAML graveyard
&lt;/h2&gt;

&lt;p&gt;This was my deploy workflow. See if any of this feels familiar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to Production&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20'&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure AWS credentials&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;aws-access-key-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_ACCESS_KEY_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;aws-secret-access-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login to Amazon ECR&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;login-ecr&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/amazon-ecr-login@v1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Docker image&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ECR_REGISTRY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.login-ecr.outputs.registry }}&lt;/span&gt;
          &lt;span class="na"&gt;IMAGE_TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.sha }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;docker build -t $ECR_REGISTRY/my-app:$IMAGE_TAG .&lt;/span&gt;
          &lt;span class="s"&gt;docker push $ECR_REGISTRY/my-app:$IMAGE_TAG&lt;/span&gt;
          &lt;span class="s"&gt;echo "IMAGE=$ECR_REGISTRY/my-app:$IMAGE_TAG" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Download task definition&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;aws ecs describe-task-definition --task-definition my-app \&lt;/span&gt;
            &lt;span class="s"&gt;--query taskDefinition &amp;gt; task-definition.json&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Update ECS task definition&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;task-def&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/amazon-ecs-render-task-definition@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;task-definition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;task-definition.json&lt;/span&gt;
          &lt;span class="na"&gt;container-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-app&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.IMAGE }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to ECS&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/amazon-ecs-deploy-task-definition@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;task-definition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.task-def.outputs.task-definition }}&lt;/span&gt;
          &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-app-service&lt;/span&gt;
          &lt;span class="na"&gt;cluster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-app-cluster&lt;/span&gt;
          &lt;span class="na"&gt;wait-for-service-stability&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Notify on failure&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;failure()&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;curl -X POST ${{ secrets.SLACK_WEBHOOK_URL }} \&lt;/span&gt;
            &lt;span class="s"&gt;-H 'Content-type: application/json' \&lt;/span&gt;
            &lt;span class="s"&gt;--data '{"text":"Deploy failed! Check Actions."}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wrote this. I'm not proud of it.&lt;/p&gt;

&lt;p&gt;The real problem wasn't the YAML itself. The problem was everything hidden &lt;em&gt;underneath&lt;/em&gt; the YAML:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An ECR repository I had to provision manually&lt;/li&gt;
&lt;li&gt;An ECS cluster, service, and task definition I had to set up in the console&lt;/li&gt;
&lt;li&gt;IAM roles with the exact right permissions (I guessed wrong twice)&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Dockerfile&lt;/code&gt; I maintained separately&lt;/li&gt;
&lt;li&gt;AWS credentials rotated manually every 90 days&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pipeline was the visible part. The invisible part was 3 days of setup I did 18 months ago that I could no longer remember well enough to recreate.&lt;/p&gt;

&lt;p&gt;When a new teammate joined and asked "how does deploy work?" — I sent them the workflow file and said "it's complicated."&lt;/p&gt;

&lt;p&gt;That's not an answer. That's a warning sign.&lt;/p&gt;

&lt;h2&gt;
  
  
  The breaking point
&lt;/h2&gt;

&lt;p&gt;In February, I switched from Node 18 to Node 20. The Docker build broke because my base image was pinned to &lt;code&gt;node:18-alpine&lt;/code&gt; in three different places — the Dockerfile, the Actions workflow, and a &lt;code&gt;.nvmrc&lt;/code&gt; file I had forgotten existed.&lt;/p&gt;

&lt;p&gt;The fix took 45 minutes. The error message was not helpful. I fixed it by diffing my Dockerfile against a Stack Overflow answer from 2023.&lt;/p&gt;

&lt;p&gt;Two weeks later, AWS deprecated the &lt;code&gt;amazon-ecs-render-task-definition@v1&lt;/code&gt; action. The pipeline broke silently — it ran, reported success, but the new image never actually deployed. I found out because a user filed a bug for something I had &lt;em&gt;definitely&lt;/em&gt; already fixed.&lt;/p&gt;

&lt;p&gt;That was the moment I decided: the pipeline is not worth maintaining.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I tried first
&lt;/h2&gt;

&lt;p&gt;I looked at Render and Railway. Both are good products. Neither deploys into my own AWS account — they provision their own infrastructure. My company has a compliance requirement that customer data stays in a customer-owned AWS environment. So those were out.&lt;/p&gt;

&lt;p&gt;I looked at AWS CodePipeline. I wanted to solve complexity, not add more of it.&lt;/p&gt;

&lt;p&gt;Then a colleague mentioned &lt;a href="https://nexusai.run" rel="noopener noreferrer"&gt;NEXUS AI&lt;/a&gt;. He described it as "a CLI that handles all the ECS stuff so you don't have to." I was skeptical. That's what everyone says.&lt;/p&gt;

&lt;h2&gt;
  
  
  The initial deploy
&lt;/h2&gt;

&lt;p&gt;I installed the CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @nexusai/cli
nexus login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I pointed it at my repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/myorg/my-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; aws_ecs_fargate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I expected this to fail immediately. My expectations for new DevOps tools are calibrated by years of experience.&lt;/p&gt;

&lt;p&gt;It didn't fail. Four and a half minutes later I got back a URL. The app was running. The same app. In my AWS account.&lt;/p&gt;

&lt;p&gt;I checked the AWS console out of habit. There was an ECS cluster. A task definition. A service. An ECR repository with the image in it. NEXUS AI had provisioned all of it.&lt;/p&gt;

&lt;p&gt;I had not written a Dockerfile. I had not configured any IAM roles. I had not touched the AWS console.&lt;/p&gt;

&lt;p&gt;I sat with that for a moment.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually happens under the hood
&lt;/h2&gt;

&lt;p&gt;Here's what &lt;code&gt;nexus deploy source&lt;/code&gt; does, in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reads your repo&lt;/strong&gt; — detects the runtime from &lt;code&gt;package.json&lt;/code&gt;, &lt;code&gt;requirements.txt&lt;/code&gt;, &lt;code&gt;go.mod&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Builds the container&lt;/strong&gt; on NEXUS AI's build infrastructure — not your machine, not a GitHub runner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pushes the image&lt;/strong&gt; to an ECR repository it provisions in your account&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creates (or updates) the ECS infrastructure&lt;/strong&gt; — cluster, task definition, service, load balancer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issues a TLS certificate&lt;/strong&gt; via ACM and wires it to the load balancer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Waits for health checks&lt;/strong&gt; to pass before returning the live URL&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Steps 3–5 are the 3 days of manual work I did 18 months ago. They now run in parallel and take about 3 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The new GitHub Actions workflow
&lt;/h2&gt;

&lt;p&gt;Here's my deploy workflow today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to Production&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20'&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;

  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nexus deploy redeploy --deployment-id ${{ secrets.NEXUSAI_DEPLOYMENT_ID }}&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;NEXUSAI_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.NEXUSAI_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;24 lines, including &lt;code&gt;name:&lt;/code&gt; fields and blank lines.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;deploy&lt;/code&gt; job has one step. It calls &lt;code&gt;nexus deploy redeploy&lt;/code&gt;, which tells NEXUS AI to rebuild from the latest commit and roll it out with a rolling update. No Docker commands. No AWS credentials. No ECR. No ECS task definition wrangling.&lt;/p&gt;

&lt;p&gt;I kept the &lt;code&gt;test&lt;/code&gt; job. NEXUS AI doesn't replace your test suite — it replaces everything &lt;em&gt;after&lt;/em&gt; tests pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Secrets and environment variables
&lt;/h2&gt;

&lt;p&gt;Before, I had secrets in three places: GitHub Actions secrets (for the pipeline), AWS Secrets Manager (for the app), and a &lt;code&gt;.env.example&lt;/code&gt; file that was always slightly out of date.&lt;/p&gt;

&lt;p&gt;Now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus secret &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres://user:pass@host/db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;STRIPE_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk_live_... &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;NODE_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are encrypted at rest and injected as environment variables when the container starts. The pipeline only needs &lt;code&gt;NEXUSAI_TOKEN&lt;/code&gt; — one secret instead of seven.&lt;/p&gt;

&lt;p&gt;After updating secrets, one command applies them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy redeploy &lt;span class="nt"&gt;--deployment-id&lt;/span&gt; &amp;lt;your-deployment-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rollback
&lt;/h2&gt;

&lt;p&gt;Old workflow rollback: figure out the previous image SHA, manually update the ECS task definition, trigger a new deployment, hope the old image hasn't been cleaned up by the ECR lifecycle policy.&lt;/p&gt;

&lt;p&gt;New rollback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy rollback &lt;span class="nt"&gt;--deployment-id&lt;/span&gt; &amp;lt;your-deployment-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reverts to the previous container image. Health checks run. Done. I've used this twice. Both times took under 90 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redeployment speed
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;First deploy:&lt;/strong&gt; ~4.5 minutes (infrastructure provisioning included).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subsequent deploys:&lt;/strong&gt; 60–90 seconds. Infrastructure is already provisioned, so it's just build → push → rolling update.&lt;/p&gt;

&lt;p&gt;My old pipeline took 10–12 minutes. Most of that was the Docker build running on GitHub's shared runners plus the ECS service stability wait.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I lost
&lt;/h2&gt;

&lt;p&gt;Every tool has trade-offs. These are the real ones:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Less visibility into the build environment.&lt;/strong&gt; With a Dockerfile I wrote, I knew exactly what was in the image. With source-based deployment, NEXUS AI generates the image. You can inspect it — &lt;code&gt;nexus deploy logs&lt;/code&gt; gives the full build output — but you're not authoring the Dockerfile. For most apps this is fine. If you have specific system dependencies (custom C extensions, obscure shared libraries), test carefully.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first deploy takes time.&lt;/strong&gt; Infrastructure provisioning isn't instant. If you need sub-30-second cold deploys for some reason, this isn't that. But once infrastructure exists, redeployments are fast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You're adding a dependency.&lt;/strong&gt; NEXUS AI is now in your deploy path. Worth knowing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Old workflow&lt;/th&gt;
&lt;th&gt;New workflow&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lines of YAML&lt;/td&gt;
&lt;td&gt;87&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pipeline runtime&lt;/td&gt;
&lt;td&gt;10–12 min&lt;/td&gt;
&lt;td&gt;60–90 sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS console setup&lt;/td&gt;
&lt;td&gt;~3 days (one-time)&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secrets locations&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rollback steps&lt;/td&gt;
&lt;td&gt;~6 manual steps&lt;/td&gt;
&lt;td&gt;1 command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Last random breakage&lt;/td&gt;
&lt;td&gt;November&lt;/td&gt;
&lt;td&gt;Hasn't happened&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How to try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @nexusai/cli

&lt;span class="c"&gt;# Authenticate&lt;/span&gt;
nexus login

&lt;span class="c"&gt;# First deploy — detects Node/Python/Go automatically, no Dockerfile needed&lt;/span&gt;
nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/your/repo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; aws_ecs_fargate   &lt;span class="c"&gt;# or gcp_cloud_run, azure_container_apps&lt;/span&gt;

&lt;span class="c"&gt;# Check status&lt;/span&gt;
nexus deploy status &lt;span class="nt"&gt;--deployment-id&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;# Set environment variables&lt;/span&gt;
nexus secret &lt;span class="nb"&gt;set &lt;/span&gt;&lt;span class="nv"&gt;KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;value &lt;span class="nv"&gt;KEY2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;value2

&lt;span class="c"&gt;# Redeploy (use this in CI)&lt;/span&gt;
nexus deploy redeploy &lt;span class="nt"&gt;--deployment-id&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;# Rollback&lt;/span&gt;
nexus deploy rollback &lt;span class="nt"&gt;--deployment-id&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For CI/CD: add &lt;code&gt;NEXUSAI_TOKEN&lt;/code&gt; and &lt;code&gt;NEXUSAI_DEPLOYMENT_ID&lt;/code&gt; as secrets in your GitHub repo settings, then replace your deploy steps with the one-liner from the workflow above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;The 87-line YAML file wasn't the real cost. The real cost was the cognitive overhead of owning it — the 45-minute debugging session when Node versions drifted, the silent failure when an Action was deprecated, the "it's complicated" I sent to a new teammate.&lt;/p&gt;

&lt;p&gt;I don't miss any of that.&lt;/p&gt;

&lt;p&gt;If you're maintaining a pipeline like the one I had, it's worth spending 20 minutes to find out how much of it you can delete.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Building something and want to compare notes? Drop it in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Full-Stack Deployment on NEXUS AI: Frontend, Backend, and Database in One Command</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Sun, 09 Aug 2026 13:15:20 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/full-stack-deployment-on-nexus-ai-frontend-backend-and-database-in-one-command-509o</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/full-stack-deployment-on-nexus-ai-frontend-backend-and-database-in-one-command-509o</guid>
      <description>&lt;p&gt;Shipping a full-stack application used to mean writing a Compose file by hand, generating passwords, wiring service names into environment variables, and hoping the health checks fired in the right order. NEXUS AI handles all of that. One command deploys your code alongside a database, connects them on a private network, and injects the credentials your app needs to connect.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;When you deploy from source using &lt;code&gt;nexusai_deploy_source&lt;/code&gt; (or &lt;code&gt;nexus deploy source&lt;/code&gt; in the CLI) with a &lt;code&gt;services&lt;/code&gt; array, NEXUS AI calls &lt;code&gt;composeService.ts&lt;/code&gt; internally to generate a &lt;code&gt;docker-compose.yml&lt;/code&gt; for your stack. Every service runs in the same private Docker network (&lt;code&gt;deploy-network-{deploymentId}&lt;/code&gt;), so your app container reaches the database by service name rather than IP address.&lt;/p&gt;

&lt;p&gt;Database volumes are named &lt;code&gt;{serviceType}-data-{deploymentId}&lt;/code&gt; and persist across container restarts and redeploys. Your data survives unless you explicitly destroy the deployment.&lt;/p&gt;

&lt;p&gt;Health checks gate startup. Your application container does not start until the database passes its readiness check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PostgreSQL: &lt;code&gt;pg_isready&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;MySQL: &lt;code&gt;mysqladmin ping&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;MongoDB: &lt;code&gt;mongosh --eval db.adminCommand("ping")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Redis: &lt;code&gt;redis-cli ping&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means your app never boots into a state where the database is still initializing.&lt;/p&gt;

&lt;p&gt;Database credentials are generated at deploy time with random passwords. You do not set them. NEXUS AI injects them directly into your containers as environment variables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Supported database services
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Image&lt;/th&gt;
&lt;th&gt;Default version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;postgres&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;15-alpine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mysql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;8.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MongoDB&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mongo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;&lt;code&gt;redis&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;7-alpine&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Deploying from source with a database
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Via the CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/your/repo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--services&lt;/span&gt; postgresql &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--environment&lt;/span&gt; production &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; docker
  &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiple services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy &lt;span class="nb"&gt;source &lt;/span&gt;https://github.com/your/repo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--services&lt;/span&gt; postgresql,redis &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--environment&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Via the MCP tool
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nexusai_deploy_source"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"repoUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/your/repo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PRODUCTION"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"services"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"postgresql"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"redis"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NEXUS AI clones the repo, builds the image, generates the Compose file, and brings everything up in one job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to the database from your app
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Auto-injected environment variables
&lt;/h3&gt;

&lt;p&gt;When NEXUS AI provisions a database service, it injects connection variables into every container in the stack automatically. You do not configure these. Read them from &lt;code&gt;process.env&lt;/code&gt; (Node.js), &lt;code&gt;os.environ&lt;/code&gt; (Python), or however your framework reads environment variables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;POSTGRES_HOST&lt;/span&gt;      &lt;span class="err"&gt;postgresql&lt;/span&gt;
&lt;span class="err"&gt;POSTGRES_PORT&lt;/span&gt;      &lt;span class="err"&gt;5432&lt;/span&gt;
&lt;span class="err"&gt;POSTGRES_DB&lt;/span&gt;        &lt;span class="err"&gt;appdb&lt;/span&gt;
&lt;span class="err"&gt;POSTGRES_USER&lt;/span&gt;      &lt;span class="err"&gt;appuser&lt;/span&gt;
&lt;span class="err"&gt;POSTGRES_PASSWORD&lt;/span&gt;  &lt;span class="err"&gt;&amp;lt;generated&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Node.js example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Pool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;POSTGRES_HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;POSTGRES_PORT&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prisma &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgresql://&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;@&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;POSTGRES_HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;POSTGRES_PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or inline in &lt;code&gt;schema.prisma&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;MySQL&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;MYSQL_HOST&lt;/span&gt;      &lt;span class="err"&gt;mysql&lt;/span&gt;
&lt;span class="err"&gt;MYSQL_PORT&lt;/span&gt;      &lt;span class="err"&gt;3306&lt;/span&gt;
&lt;span class="err"&gt;MYSQL_DATABASE&lt;/span&gt;  &lt;span class="err"&gt;appdb&lt;/span&gt;
&lt;span class="err"&gt;MYSQL_USER&lt;/span&gt;      &lt;span class="err"&gt;appuser&lt;/span&gt;
&lt;span class="err"&gt;MYSQL_PASSWORD&lt;/span&gt;  &lt;span class="err"&gt;&amp;lt;generated&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Node.js example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mysql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mysql2/promise&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createPool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MYSQL_HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MYSQL_PORT&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;MongoDB&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;MONGO_HOST&lt;/span&gt;      &lt;span class="err"&gt;mongodb&lt;/span&gt;
&lt;span class="err"&gt;MONGO_PORT&lt;/span&gt;      &lt;span class="err"&gt;27017&lt;/span&gt;
&lt;span class="err"&gt;MONGO_DATABASE&lt;/span&gt;  &lt;span class="err"&gt;appdb&lt;/span&gt;
&lt;span class="err"&gt;MONGO_USERNAME&lt;/span&gt;  &lt;span class="err"&gt;root&lt;/span&gt;
&lt;span class="err"&gt;MONGO_PASSWORD&lt;/span&gt;  &lt;span class="err"&gt;&amp;lt;generated&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mongoose example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`mongodb://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MONGO_USERNAME&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MONGO_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`@&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MONGO_HOST&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MONGO_PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MONGO_DATABASE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Redis&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;REDIS_HOST&lt;/span&gt;  &lt;span class="err"&gt;redis&lt;/span&gt;
&lt;span class="err"&gt;REDIS_PORT&lt;/span&gt;  &lt;span class="err"&gt;6379&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ioredis example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ioredis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIS_HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIS_PORT&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The host in every case is the service name on the internal Docker network. From inside your container, &lt;code&gt;postgresql&lt;/code&gt; resolves to the Postgres container, &lt;code&gt;mysql&lt;/code&gt; to MySQL, and so on. These hostnames are not reachable from outside the deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the secrets vault for external databases
&lt;/h2&gt;

&lt;p&gt;If you connect to a database that runs outside NEXUS AI (a managed RDS instance, a MongoDB Atlas cluster, a Redis Cloud endpoint), use the secrets vault instead of hardcoding credentials.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Create a secret&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus secret create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; DATABASE_URL &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--value&lt;/span&gt; &lt;span class="s2"&gt;"postgresql://user:password@rds.amazonaws.com:5432/mydb"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--environment&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Link it to your deployment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pass the secret name when deploying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy &lt;span class="nb"&gt;source &lt;/span&gt;https://github.com/your/repo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--secret-ids&lt;/span&gt; &amp;lt;secret-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At deploy time, NEXUS AI decrypts the secret (AES-256-GCM) and injects it as an environment variable into the container. Secrets do not override variables you pass explicitly in &lt;code&gt;--env&lt;/code&gt;. The lookup order is: explicit env vars first, then secrets.&lt;/p&gt;

&lt;p&gt;Your application reads &lt;code&gt;process.env.DATABASE_URL&lt;/code&gt; exactly as it would for any other environment variable. There is nothing special to configure in your code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rotating a secret&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus secret update &lt;span class="nt"&gt;--id&lt;/span&gt; &amp;lt;secret-id&amp;gt; &lt;span class="nt"&gt;--value&lt;/span&gt; &lt;span class="s2"&gt;"new-connection-string"&lt;/span&gt;
nexus deploy redeploy &amp;lt;deployment-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new value takes effect on the next deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend deployment
&lt;/h2&gt;

&lt;p&gt;NEXUS AI Docker deployments run a single application container (plus database sidecar services). There is no built-in multi-container split for frontend and backend today.&lt;/p&gt;

&lt;p&gt;Two patterns work:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 1: Serve the frontend from the backend&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Build your frontend and serve the static files from your backend server. This is the simplest approach and fits most full-stack frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js&lt;/strong&gt; — runs as a single Node.js server (&lt;code&gt;next start&lt;/code&gt;) that handles both SSR and API routes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Express + React&lt;/strong&gt; — build the React app into &lt;code&gt;dist/&lt;/code&gt;, then serve it with &lt;code&gt;express.static&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Django + Vue&lt;/strong&gt; — run &lt;code&gt;npm run build&lt;/code&gt; in your Dockerfile, copy &lt;code&gt;dist/&lt;/code&gt; into Django's static files directory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your Dockerfile handles the build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;frontend-build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app/frontend&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; frontend/package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; frontend/ ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:20-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; backend/package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--production&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; backend/ ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=frontend-build /app/frontend/dist ./public&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "src/index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pattern 2: Deploy frontend separately&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deploy the frontend as a separate NEXUS AI deployment (static site or Node.js server), and point it at the backend's public URL. Use &lt;code&gt;nexus deploy status &amp;lt;backend-name&amp;gt;&lt;/code&gt; to get the backend URL, then pass it as an environment variable to the frontend deployment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get backend URL&lt;/span&gt;
&lt;span class="nv"&gt;BACKEND_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;nexus deploy status my-api &lt;span class="nt"&gt;--json&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.url'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Deploy frontend pointing at backend&lt;/span&gt;
nexus deploy &lt;span class="nb"&gt;source &lt;/span&gt;https://github.com/your/repo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-frontend &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;VITE_API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$BACKEND_URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What gets deployed
&lt;/h2&gt;

&lt;p&gt;After &lt;code&gt;nexus deploy source&lt;/code&gt; completes with a database service, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your application container running on a public NEXUS AI subdomain&lt;/li&gt;
&lt;li&gt;One or more database containers on a private internal network&lt;/li&gt;
&lt;li&gt;Named volumes for each database (data persists across redeploys)&lt;/li&gt;
&lt;li&gt;Auto-generated credentials injected as environment variables&lt;/li&gt;
&lt;li&gt;Health checks configured and active&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;lastBackup&lt;/code&gt; field ready for daily automated backups (see the backup guide)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run &lt;code&gt;nexus deploy status &amp;lt;name&amp;gt;&lt;/code&gt; to see the full picture: URL, status, attached services, and their connection metadata.&lt;/p&gt;

</description>
      <category>deployment</category>
      <category>docker</category>
      <category>fullstack</category>
      <category>database</category>
    </item>
    <item>
      <title>Database Backup and Restore in NEXUS AI: A Complete Guide</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Sun, 09 Aug 2026 13:13:04 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/database-backup-and-restore-in-nexus-ai-a-complete-guide-47f4</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/database-backup-and-restore-in-nexus-ai-a-complete-guide-47f4</guid>
      <description>&lt;p&gt;Every production deployment needs a data recovery plan. NEXUS AI ships built-in backup and restore for all four supported database engines: PostgreSQL, MySQL, MongoDB, and Redis. This guide covers three ways to use it: the dashboard, the CLI, and direct API calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  What gets backed up
&lt;/h2&gt;

&lt;p&gt;When you deploy a full-stack application on NEXUS AI with a database service, the platform manages the database container alongside your app in a private Docker network. Backups capture a consistent snapshot of that data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL&lt;/strong&gt; — uses &lt;code&gt;pg_dump&lt;/code&gt; in custom format (&lt;code&gt;.dump&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MySQL&lt;/strong&gt; — uses &lt;code&gt;mysqldump&lt;/code&gt; SQL export (&lt;code&gt;.sql&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MongoDB&lt;/strong&gt; — uses &lt;code&gt;mongodump&lt;/code&gt; compressed archive (&lt;code&gt;.archive&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis&lt;/strong&gt; — uses the native RDB snapshot (&lt;code&gt;.rdb&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All backup files are stored server-side and tracked in the NEXUS AI platform. Each backup record shows the engine type, file size, status, and creation time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the CLI
&lt;/h2&gt;

&lt;p&gt;Install or update the CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; nexusapp-cli
nexus &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create a backup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup &amp;lt;service-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command streams progress and prints a summary when done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✔ Backup created
  ID:      3f8a1c2d-...
  Type:    postgresql
  Size:    4.2 MB
  Created: just now
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find your &lt;code&gt;service-id&lt;/code&gt; by running &lt;code&gt;nexus deploy status &amp;lt;deployment-name&amp;gt;&lt;/code&gt; and looking at the attached services table.&lt;/p&gt;

&lt;h3&gt;
  
  
  List backups
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backups &amp;lt;service-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ID                                    TYPE          SIZE      STATUS     CREATED
3f8a1c2d-...                          postgresql    4.2 MB    completed  2m ago
1a2b3c4e-...                          postgresql    3.9 MB    completed  1d ago
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Restore from a backup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db restore &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will be prompted to confirm before the restore runs — this overwrites current data. Use &lt;code&gt;--yes&lt;/code&gt; to skip the prompt in scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db restore &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restoring PostgreSQL and MySQL replays all data with &lt;code&gt;--clean&lt;/code&gt; / &lt;code&gt;--drop&lt;/code&gt; semantics so the target schema is wiped before the restore. MongoDB uses &lt;code&gt;--drop&lt;/code&gt; per collection. Redis stops the container, swaps the RDB file, and restarts — expect a few seconds of downtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delete a backup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-delete &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This removes both the file on disk and the tracking record. Deletion is permanent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable daily automated backups
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-schedule &amp;lt;service-id&amp;gt; &lt;span class="nt"&gt;--enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once enabled, NEXUS AI runs a backup every 24 hours automatically. To disable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-schedule &amp;lt;service-id&amp;gt; &lt;span class="nt"&gt;--disable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the REST API
&lt;/h2&gt;

&lt;p&gt;All backup operations are also available over HTTP. Authenticate with a Bearer token from &lt;code&gt;nexus auth token create&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a backup&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://nexusai.run/api/deployment-services/&amp;lt;service-id&amp;gt;/backup &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;List backups&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://nexusai.run/api/deployment-services/&amp;lt;service-id&amp;gt;/backups &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Restore&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://nexusai.run/api/deployment-services/&amp;lt;service-id&amp;gt;/restore &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"backupId":"&amp;lt;backup-id&amp;gt;"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Toggle scheduled backups&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; PATCH https://nexusai.run/api/deployment-services/&amp;lt;service-id&amp;gt;/backup/schedule &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"enabled":true}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Disaster recovery playbook
&lt;/h2&gt;

&lt;p&gt;Here is a practical runbook for restoring after data loss:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. List your recent backups&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backups &amp;lt;service-id&amp;gt; &lt;span class="nt"&gt;--json&lt;/span&gt; | jq &lt;span class="s1"&gt;'.[0:3]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pick the most recent backup with &lt;code&gt;"status": "completed"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Verify the backup exists&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backups &amp;lt;service-id&amp;gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &amp;lt;backup-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Run the restore&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db restore &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Verify your application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check your app is healthy after the restore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy status &amp;lt;deployment-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch for the health check to return &lt;code&gt;healthy&lt;/code&gt; before routing traffic back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up automated backups (recommended)
&lt;/h2&gt;

&lt;p&gt;For any production database, enable daily backups immediately after deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Deploy your app&lt;/span&gt;
nexus deploy &lt;span class="nb"&gt;source &lt;/span&gt;https://github.com/your/repo &lt;span class="nt"&gt;--name&lt;/span&gt; my-app

&lt;span class="c"&gt;# Find the database service ID&lt;/span&gt;
nexus deploy status my-app &lt;span class="nt"&gt;--json&lt;/span&gt; | jq &lt;span class="s1"&gt;'.services[] | select(.serviceType == "postgresql") | .id'&lt;/span&gt;

&lt;span class="c"&gt;# Enable daily backups&lt;/span&gt;
nexus db backup-schedule &amp;lt;service-id&amp;gt; &lt;span class="nt"&gt;--enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NEXUS AI runs the backup job every 24 hours. The last backup time is visible in &lt;code&gt;nexus deploy status&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do before a risky migration
&lt;/h2&gt;

&lt;p&gt;Run a manual backup before any schema migration or destructive operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup &amp;lt;service-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the backup ID, run your migration, and restore from that ID if anything goes wrong. This gives you a clean rollback path without downtime planning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Engine-specific notes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt;&lt;br&gt;
Backups use &lt;code&gt;pg_dump -F c&lt;/code&gt; (custom format). Restores use &lt;code&gt;pg_restore --clean --if-exists&lt;/code&gt;, which drops and recreates all objects. The target database must exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MySQL&lt;/strong&gt;&lt;br&gt;
Backups are plain SQL dumps. Restores pipe the SQL directly into &lt;code&gt;mysql&lt;/code&gt;. All tables in the target database are replaced.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MongoDB&lt;/strong&gt;&lt;br&gt;
Backups use &lt;code&gt;mongodump&lt;/code&gt; with authentication against the &lt;code&gt;admin&lt;/code&gt; database. Restores use &lt;code&gt;mongorestore --drop&lt;/code&gt;, which removes each existing collection before importing. Only the named database is restored.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redis&lt;/strong&gt;&lt;br&gt;
Redis backups trigger a &lt;code&gt;BGSAVE&lt;/code&gt; and copy the resulting &lt;code&gt;dump.rdb&lt;/code&gt; file. Restores stop the container, replace the RDB, and restart. There is a brief connection interruption during restore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Backup files are stored on the NEXUS AI server. For off-site copies, download them via the dashboard or export endpoints.&lt;/li&gt;
&lt;li&gt;Backups reflect the state at the moment &lt;code&gt;pg_dump&lt;/code&gt; / &lt;code&gt;mysqldump&lt;/code&gt; / &lt;code&gt;mongodump&lt;/code&gt; runs. In-flight transactions may not be captured.&lt;/li&gt;
&lt;li&gt;Redis BGSAVE is non-blocking but the saved RDB may be up to a few seconds behind the latest write.&lt;/li&gt;
&lt;li&gt;Restoring to a different service ID or engine type is not supported. Restore targets the same container the backup came from.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>database</category>
      <category>backup</category>
      <category>devops</category>
      <category>docker</category>
    </item>
    <item>
      <title>Stop shipping secrets. Start using a vault.</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Sun, 09 Aug 2026 13:08:37 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/stop-shipping-secrets-start-using-a-vault-h2n</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/stop-shipping-secrets-start-using-a-vault-h2n</guid>
      <description>&lt;h1&gt;
  
  
  Stop shipping secrets. Start using a vault.
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Published:&lt;/strong&gt; April 21, 2026&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Security · DevOps&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time:&lt;/strong&gt; 12 minutes&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Author:&lt;/strong&gt; NEXUS AI Team&lt;/p&gt;



&lt;p&gt;Leaked API keys cost companies an average of $1.2M per incident. Not because engineers are careless — because the tooling makes the wrong thing easy. &lt;code&gt;.env&lt;/code&gt; files committed to repos. Hardcoded credentials baked into container images. Production secrets copy-pasted into Slack for "temporary" handoffs that last six months.&lt;/p&gt;

&lt;p&gt;NEXUS AI ships a built-in Secrets Vault and a first-class Access Token system. This post covers exactly how both work, how they integrate at deploy time, and how to run a zero-plaintext secret configuration in your production environment — starting today.&lt;/p&gt;


&lt;h2&gt;
  
  
  The problem with secrets in 2026
&lt;/h2&gt;

&lt;p&gt;Most teams manage secrets in one of three ways:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;What goes wrong&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;.env&lt;/code&gt; files in repos&lt;/td&gt;
&lt;td&gt;One &lt;code&gt;git log&lt;/code&gt; away from a breach. "Secret" scans never catch everything.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI/CD environment variables&lt;/td&gt;
&lt;td&gt;Visible to anyone with repo access. Rotate one and you're editing 14 pipelines.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud secret managers (AWS Secrets Manager, GCP Secret Manager)&lt;/td&gt;
&lt;td&gt;Right idea, wrong integration. Requires service accounts, IAM glue, and a custom fetch layer in every app.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The common thread: secrets live somewhere they shouldn't, accessed by code that has more permission than it needs, with no record of who read what.&lt;/p&gt;

&lt;p&gt;NEXUS AI eliminates all three failure modes at the platform level.&lt;/p&gt;


&lt;h2&gt;
  
  
  The NEXUS AI Secrets Vault
&lt;/h2&gt;

&lt;p&gt;Every NEXUS AI project ships with an encrypted Secrets Vault. Secrets are encrypted at rest using &lt;strong&gt;AES-256-GCM&lt;/strong&gt; before they touch the database — the same cipher used by the U.S. Department of Defense for classified data at rest.&lt;/p&gt;
&lt;h3&gt;
  
  
  How encryption works
&lt;/h3&gt;

&lt;p&gt;When you store a secret, NEXUS AI:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Encrypts the value with AES-256-GCM using a derived key (scoped per organization).&lt;/li&gt;
&lt;li&gt;Stores only the ciphertext, the IV (initialization vector), and the auth tag.&lt;/li&gt;
&lt;li&gt;Never writes the plaintext to disk, logs, or any observable surface.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The encryption key itself is never stored alongside the ciphertext. It's derived at runtime from &lt;code&gt;SECRETS_ENCRYPTION_KEY&lt;/code&gt; — an environment variable you set once at the platform level and never touch again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set via the NEXUS AI CLI&lt;/span&gt;
nexus secret &lt;span class="nb"&gt;set &lt;/span&gt;DATABASE_URL &lt;span class="s2"&gt;"postgres://user:pass@host:5432/db"&lt;/span&gt;
nexus secret &lt;span class="nb"&gt;set &lt;/span&gt;STRIPE_SECRET_KEY &lt;span class="s2"&gt;"sk_live_..."&lt;/span&gt;
nexus secret &lt;span class="nb"&gt;set &lt;/span&gt;OPENAI_API_KEY &lt;span class="s2"&gt;"sk-..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Secrets are namespaced per deployment. A secret set for &lt;code&gt;api-prod&lt;/code&gt; is not visible to &lt;code&gt;api-staging&lt;/code&gt; — even if both deployments live in the same project.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the vault does NOT do
&lt;/h3&gt;

&lt;p&gt;No system is better described by its constraints than its capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The vault does &lt;strong&gt;not&lt;/strong&gt; store secrets in application containers. Secrets are injected at runtime as environment variables, never embedded in the image.&lt;/li&gt;
&lt;li&gt;The vault does &lt;strong&gt;not&lt;/strong&gt; expose secret values through logs. The observability pipeline redacts known secret patterns automatically.&lt;/li&gt;
&lt;li&gt;The vault does &lt;strong&gt;not&lt;/strong&gt; allow cross-tenant secret access. Secrets are scoped to an organization ID enforced at the query layer — not just the API layer.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Secure Configuration Injection
&lt;/h2&gt;

&lt;p&gt;When NEXUS AI deploys your application, secrets don't get "loaded" by your app at startup. They're injected into the container's environment by the runtime before the first process starts.&lt;/p&gt;

&lt;p&gt;The sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Deploy command issued
2. NEXUS AI fetches encrypted secrets for the target deployment
3. Secrets decrypted in the control plane (never on the container host)
4. Injected as environment variables into the container spec
5. Container starts — secrets already present as env vars
6. Plaintext exists only in memory, for the lifetime of the process
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your application code reads &lt;code&gt;process.env.DATABASE_URL&lt;/code&gt; exactly as it always has. Zero changes to application code. Zero additional SDK imports. Zero IAM role configuration.&lt;/p&gt;

&lt;p&gt;This is what NEXUS AI calls &lt;strong&gt;Secure Configuration Injection&lt;/strong&gt; — vault-backed runtime config by default.&lt;/p&gt;




&lt;h2&gt;
  
  
  Access Tokens
&lt;/h2&gt;

&lt;p&gt;Secrets protect your application's config. Access Tokens protect access to NEXUS AI itself.&lt;/p&gt;

&lt;p&gt;An Access Token is a scoped credential that authenticates API calls to NEXUS AI — including calls from the CLI, CI/CD pipelines, external scripts, or AI agents running via MCP.&lt;/p&gt;

&lt;h3&gt;
  
  
  Token anatomy
&lt;/h3&gt;

&lt;p&gt;Every NEXUS AI Access Token carries three pieces of information:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;token_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stable identifier. Visible in audit logs. Safe to display.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;token_value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The actual secret. Shown once at creation. Never stored in plaintext.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scopes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;What the token is allowed to do. Read-only? Deploy-only? Full control?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Tokens are hashed with SHA-256 before storage. If an attacker gets your database, they get hashes — not tokens.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a token
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# CLI&lt;/span&gt;
nexus token create &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"ci-deploy-prod"&lt;/span&gt; &lt;span class="nt"&gt;--scopes&lt;/span&gt; deploy:write,secrets:read

&lt;span class="c"&gt;# Output:&lt;/span&gt;
&lt;span class="c"&gt;# Token ID:    tok_01HX9...&lt;/span&gt;
&lt;span class="c"&gt;# Token value: nxt_live_...  ← shown once, copy it now&lt;/span&gt;
&lt;span class="c"&gt;# Scopes:      deploy:write, secrets:read&lt;/span&gt;
&lt;span class="c"&gt;# Expires:     never (set --expires 90d to add expiry)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Or via the NEXUS AI dashboard:&lt;/span&gt;
&lt;span class="c"&gt;# Settings → Access Tokens → New Token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For CI/CD pipelines, store the token value as a repository secret in your provider (GitHub Actions, GitLab CI, etc.) and inject it as &lt;code&gt;NEXUS_API_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Token scopes
&lt;/h3&gt;

&lt;p&gt;NEXUS AI uses a least-privilege scope model. Grant exactly what the caller needs — nothing more.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;What it allows&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deploy:read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View deployment status, logs, metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deploy:write&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create, redeploy, rollback, stop deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;secrets:read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List secret names (never values)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;secrets:write&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create, update, delete secrets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;billing:read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View usage and invoices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;admin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full organization access&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A CI/CD pipeline that only needs to redeploy on merge gets &lt;code&gt;deploy:write&lt;/code&gt;. It cannot read secrets, cannot view billing, cannot create new deployments from scratch. One compromised pipeline token does not become a full organization breach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Token expiry and rotation
&lt;/h3&gt;

&lt;p&gt;Short-lived tokens are better tokens. Set expiry explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus token create &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"github-actions-main"&lt;/span&gt; &lt;span class="nt"&gt;--scopes&lt;/span&gt; deploy:write &lt;span class="nt"&gt;--expires&lt;/span&gt; 90d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rotate before expiry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus token rotate tok_01HX9...
&lt;span class="c"&gt;# Old token: revoked immediately&lt;/span&gt;
&lt;span class="c"&gt;# New token: nxt_live_...  ← 90-day window restarts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rotation is zero-downtime. The old token stays valid for a 5-minute grace window — long enough for an in-flight deploy to complete, short enough that a leaked token has minimal exposure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Secrets + Tokens in a real pipeline
&lt;/h2&gt;

&lt;p&gt;Here's what a production-grade deploy pipeline looks like with NEXUS AI:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Store secrets once
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Done once, by a developer with secrets:write scope&lt;/span&gt;
nexus secret &lt;span class="nb"&gt;set &lt;/span&gt;DATABASE_URL &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DATABASE_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
nexus secret &lt;span class="nb"&gt;set &lt;/span&gt;REDIS_URL &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REDIS_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
nexus secret &lt;span class="nb"&gt;set &lt;/span&gt;JWT_SECRET &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 32&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create a scoped CI token
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a deploy-only token for GitHub Actions&lt;/span&gt;
nexus token create &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"github-actions-main"&lt;/span&gt; &lt;span class="nt"&gt;--scopes&lt;/span&gt; deploy:write &lt;span class="nt"&gt;--expires&lt;/span&gt; 90d
&lt;span class="c"&gt;# → Store the token value in GitHub Actions secrets as NEXUS_API_KEY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: CI/CD pipeline (GitHub Actions example)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to production&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to NEXUS AI&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;NEXUS_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.NEXUS_API_KEY }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npx nexus-cli deploy redeploy --deployment api-prod&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No secrets in the pipeline YAML. No secrets in the container image. The pipeline token can trigger a redeploy — it cannot read &lt;code&gt;DATABASE_URL&lt;/code&gt;, cannot change secrets, cannot touch other projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Runtime
&lt;/h3&gt;

&lt;p&gt;When the container starts, it finds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;postgres://...&lt;/span&gt;
&lt;span class="py"&gt;REDIS_URL&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;redis://...&lt;/span&gt;
&lt;span class="py"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ready. No SDK. No fetch-on-startup. No cold-start latency penalty from secret retrieval.&lt;/p&gt;




&lt;h2&gt;
  
  
  RBAC and the audit trail
&lt;/h2&gt;

&lt;p&gt;Access Tokens operate within NEXUS AI's Role-Based Access Control (RBAC) system. Every token is issued to an organization member with a role:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;What they can do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Viewer&lt;/td&gt;
&lt;td&gt;Read logs, status, and deployment metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Developer&lt;/td&gt;
&lt;td&gt;Deploy, redeploy, rollback; read secret names&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Admin&lt;/td&gt;
&lt;td&gt;Full control including secrets, tokens, and billing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Owner&lt;/td&gt;
&lt;td&gt;Everything Admin can do + delete the organization&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A &lt;code&gt;Developer&lt;/code&gt; role member creating a token cannot grant that token &lt;code&gt;admin&lt;/code&gt; scope. Privilege escalation via token creation is blocked at the model layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  The audit log
&lt;/h3&gt;

&lt;p&gt;Every secret operation and token use is recorded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"secret.updated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"actor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"saif@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DATABASE_URL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deployment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api-prod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-21T09:14:22Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"203.0.113.45"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deploy.redeploy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"actor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tok_01HX9..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api-prod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deployment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api-prod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-21T09:17:05Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"140.82.114.3"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a deploy fires at 3 AM, you know it was the CI token. When a secret changes, you know who changed it and from where. Audit logs are append-only, exported to your observability stack, and retained for 90 days on Pro and indefinitely on Enterprise.&lt;/p&gt;




&lt;h2&gt;
  
  
  MCP: AI agents and your secrets vault
&lt;/h2&gt;

&lt;p&gt;NEXUS AI ships 37 MCP tools for Claude and other AI agents. When an AI agent uses a token to call the NEXUS AI MCP server, it operates under the same scope model as a human caller.&lt;/p&gt;

&lt;p&gt;An agent with &lt;code&gt;deploy:read&lt;/code&gt; can inspect deployment status and fetch logs. It cannot modify secrets, redeploy, or rollback — unless its token includes &lt;code&gt;deploy:write&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Agent calls nexusai_deploy_status → allowed (deploy:read)&lt;/span&gt;
&lt;span class="c"&gt;# Agent calls nexusai_secrets_create → blocked (secrets:write not granted)&lt;/span&gt;
&lt;span class="c"&gt;# Agent calls nexusai_deploy_redeploy → blocked (deploy:write not granted)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means you can give Claude read-only access to your production environment for diagnosis — and be certain it cannot modify anything. When you want Claude to act (redeploy, rollback), you issue a &lt;code&gt;deploy:write&lt;/code&gt; token explicitly, scoped to exactly that.&lt;/p&gt;

&lt;p&gt;AI agents with unconstrained production access are a liability. Scoped tokens make AI-assisted operations safe by default.&lt;/p&gt;




&lt;h2&gt;
  
  
  Checklist: zero-plaintext secrets in 10 minutes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Store all secrets with &lt;code&gt;nexus secret set&lt;/code&gt; — delete any existing &lt;code&gt;.env&lt;/code&gt; from your repo&lt;/li&gt;
&lt;li&gt;[ ] Create deployment-scoped tokens for every CI/CD pipeline — one token per pipeline&lt;/li&gt;
&lt;li&gt;[ ] Set token expiry to 90 days maximum — calendar reminders for rotation&lt;/li&gt;
&lt;li&gt;[ ] Assign team members the minimum role they need — Viewers don't need Developer&lt;/li&gt;
&lt;li&gt;[ ] Enable audit log export to your SIEM or logging stack (Datadog, Grafana, etc.)&lt;/li&gt;
&lt;li&gt;[ ] Run &lt;code&gt;nexus secret list&lt;/code&gt; quarterly — delete secrets that no longer have a deployment&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Can I view a secret value after I've stored it?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No. The vault stores only the encrypted ciphertext. The plaintext value is shown once at creation. This is intentional — if you need to verify a secret, rotate it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens to secrets if I delete a deployment?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Secrets are scoped to a deployment. Deleting the deployment marks its secrets as orphaned. Orphaned secrets are purged automatically after 30 days, or immediately if you run &lt;code&gt;nexus secret purge --deployment &amp;lt;name&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I handle secrets that multiple deployments share?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Set the secret on each deployment. NEXUS AI does not support cross-deployment secret references by design — shared access is shared blast radius. Duplication is cheaper than a cross-project breach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can a Developer-role member read secret values?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No. &lt;code&gt;secrets:read&lt;/code&gt; scope lists secret names (e.g., &lt;code&gt;DATABASE_URL&lt;/code&gt;) but never values. Only the NEXUS AI control plane decrypts values — and only at deploy time, into the container environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What encryption key does NEXUS AI use?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
On NEXUS AI-managed infrastructure, the encryption key is managed by the platform and rotated automatically. On Enterprise On-Prem, you supply &lt;code&gt;SECRETS_ENCRYPTION_KEY&lt;/code&gt; and own the key lifecycle entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I use NEXUS AI secrets with a non-NEXUS AI host?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Not directly. The vault is tightly integrated with the deployment runtime. If you're running on external infrastructure, use your cloud provider's native secret manager and inject credentials at the infrastructure level.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Secrets Vault and Access Tokens ship on every NEXUS AI plan — including Starter at $29/mo. No additional configuration, no third-party integrations, no IAM policy documents.&lt;/p&gt;

&lt;p&gt;If you're on an Enterprise or regulated workload, RBAC, audit log retention, and on-prem key management are available on Enterprise and Enterprise On-Prem tiers. Talk to the team at &lt;a href="https://nexusai.run" rel="noopener noreferrer"&gt;nexusai.run&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Related reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How NEXUS AI deploys your app in under 5 minutes&lt;/li&gt;
&lt;li&gt;RBAC deep dive: roles, scopes, and least privilege&lt;/li&gt;
&lt;li&gt;MCP integration: 37 tools for Claude and AI agents&lt;/li&gt;
&lt;li&gt;Audit logs and compliance: what gets recorded and why&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Ship what matters. Lock down the rest.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>api</category>
      <category>productivity</category>
    </item>
    <item>
      <title>From v0 prototype to production database in 5 minutes</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Sun, 09 Aug 2026 13:05:36 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/from-v0-prototype-to-production-database-in-5-minutes-4a3c</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/from-v0-prototype-to-production-database-in-5-minutes-4a3c</guid>
      <description>&lt;h1&gt;
  
  
  From v0 prototype to production database in 5 minutes
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Published:&lt;/strong&gt; May 17, 2026&lt;br&gt;
&lt;strong&gt;Category:&lt;/strong&gt; AI · Next.js · Databases&lt;br&gt;
&lt;strong&gt;Reading time:&lt;/strong&gt; 8 minutes&lt;br&gt;
&lt;strong&gt;Author:&lt;/strong&gt; NEXUS AI Team&lt;/p&gt;



&lt;p&gt;v0 by Vercel generates a beautiful React component in 60 seconds. Lovable, Bolt, and Replit do the same for whole frontends. The result looks production-ready in the browser.&lt;/p&gt;

&lt;p&gt;Then you try to save a user. There is no database. There is no auth backend. There is no file storage. The "deploy" button shipped a frontend with a fetch call pointed at a URL that does not exist yet.&lt;/p&gt;

&lt;p&gt;This is the deploy gap for prototyping tools, and it is where most teams either give up or end up wiring three SaaS dashboards together. This post shows the third path: take the v0 prototype, give it a real Postgres backend, attach a bucket for uploads, and have the whole stack running in about 5 minutes.&lt;/p&gt;

&lt;p&gt;For the broader picture of why AI-generated apps need a different deploy story, see &lt;a href="https://nexusai.run/blog/your-ai-app-is-generated-now-how-do-you-deploy-it" rel="noopener noreferrer"&gt;Your AI app is generated. Now how do you deploy it?&lt;/a&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  What v0 (and friends) actually ship
&lt;/h2&gt;

&lt;p&gt;The current generation of AI prototyping tools ship a Next.js frontend with hard-coded data, or wire it to a serverless route file with mock responses. You can run it locally. You can deploy the frontend to Vercel. What you do not get out of the box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A managed database with persistence (Postgres, MySQL, Mongo).&lt;/li&gt;
&lt;li&gt;An object store for image uploads, model artifacts, or generated assets.&lt;/li&gt;
&lt;li&gt;A background worker for async tasks.&lt;/li&gt;
&lt;li&gt;Secrets management for API keys.&lt;/li&gt;
&lt;li&gt;Backups and a restore path.&lt;/li&gt;
&lt;li&gt;Logs you can stream.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a real app you need all of these. For an AI-built side project you usually need at least the first two.&lt;/p&gt;


&lt;h2&gt;
  
  
  Three ways to add a real backend
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Vercel Postgres + Vercel KV.&lt;/strong&gt; Convenient if your frontend is already on Vercel. Adds a second billing line, a separate IAM model, and locks the data to Vercel's region story. No background workers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bring-your-own Supabase, Neon, or PlanetScale.&lt;/strong&gt; Best-in-class managed databases. You still own the wiring between the database, your frontend, your storage provider (separate again), your queue provider (separate again), and your secrets vault. Three dashboards minimum, sometimes five.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NEXUS AI.&lt;/strong&gt; One deploy gives you a containerized backend, a managed Postgres, optional Redis, an S3-compatible bucket, encrypted secrets, backups, scaling, and a public URL. One dashboard. One CLI. One MCP server your AI agent can drive end-to-end.&lt;/p&gt;

&lt;p&gt;This post walks through the third path because the others are well documented elsewhere.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 1: export the v0 frontend
&lt;/h2&gt;

&lt;p&gt;v0 already supports &lt;code&gt;Add to codebase&lt;/code&gt;. Push the result to a GitHub repo. The frontend will look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-v0-app/
  app/
    layout.tsx
    page.tsx
    api/
      users/
        route.ts        ← currently returns mock data
      uploads/
        route.ts        ← currently returns a fake URL
  package.json
  next.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two route files are where we replace the mocks. We will leave them in the same Next.js app rather than building a separate Express backend. v0 + Next.js + Postgres + S3 is a complete production stack on its own.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: wire the route handlers to real services
&lt;/h2&gt;

&lt;p&gt;Update &lt;code&gt;app/api/users/route.ts&lt;/code&gt; to talk to Postgres:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Pool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATABASE_URL&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT id, email, created_at FROM users ORDER BY created_at DESC LIMIT 50&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INSERT INTO users (email) VALUES ($1) RETURNING id, email, created_at&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update &lt;code&gt;app/api/uploads/route.ts&lt;/code&gt; to push to the S3-compatible bucket NEXUS AI provisions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PutObjectCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/client-s3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;randomUUID&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_REGION&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;forcePathStyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a tiny migration that creates the &lt;code&gt;users&lt;/code&gt; table. Put it in &lt;code&gt;db/init.sql&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;          &lt;span class="nb"&gt;serial&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;email&lt;/span&gt;       &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt;  &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commit and push.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: deploy the whole stack with one command
&lt;/h2&gt;

&lt;p&gt;Install the NEXUS AI CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://nexusai.run/install.sh | bash    &lt;span class="c"&gt;# Linux&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://nexusai.run/install-mac.sh | bash &lt;span class="c"&gt;# macOS&lt;/span&gt;
nexus auth login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy the app, Postgres, and a bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/you/my-v0-app.git &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-v0-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; docker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--framework&lt;/span&gt; nextjs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--services&lt;/span&gt; postgresql &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create and attach a bucket for uploads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus bucket create user-uploads &lt;span class="nt"&gt;--display-name&lt;/span&gt; &lt;span class="s2"&gt;"User uploads"&lt;/span&gt;
nexus bucket attach &amp;lt;bucket-id&amp;gt; &amp;lt;deployment-id&amp;gt;
nexus deploy redeploy &amp;lt;deployment-id&amp;gt; &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The platform builds the Next.js production image, provisions a Postgres 15 container with a persistent volume, generates a scoped per-bucket IAM service account, injects the environment variables (&lt;code&gt;DATABASE_URL&lt;/code&gt;, &lt;code&gt;S3_ENDPOINT&lt;/code&gt;, &lt;code&gt;S3_BUCKET&lt;/code&gt;, &lt;code&gt;S3_ACCESS_KEY&lt;/code&gt;, &lt;code&gt;S3_SECRET_KEY&lt;/code&gt;), opens a public HTTPS URL through Traefik, and starts streaming logs.&lt;/p&gt;

&lt;p&gt;Run the schema migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db query &amp;lt;postgres-service-id&amp;gt; &lt;span class="nt"&gt;--file&lt;/span&gt; db/init.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: verify the full path
&lt;/h2&gt;

&lt;p&gt;Hit the deployed URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://your-app.nexusai.run/api/users &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"email":"first@example.com"}'&lt;/span&gt;

curl https://your-app.nexusai.run/api/users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected response on the GET:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"first@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-17T03:14:22.117Z"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upload a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://your-app.nexusai.run/api/uploads &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"file=@./avatar.png"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9b4e2a01-avatar.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://minio.nexusai.run/user-uploads/9b4e2a01-avatar.png"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You now have a v0-generated frontend talking to a real Postgres and a real S3 bucket. No third dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: take the first backup
&lt;/h2&gt;

&lt;p&gt;The first ops habit worth installing on day one is backups. Take a snapshot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db services my-v0-app
nexus db backup &amp;lt;postgres-service-id&amp;gt;
nexus db backups &amp;lt;postgres-service-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schedule recurring backups:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-schedule &amp;lt;postgres-service-id&amp;gt; &lt;span class="nt"&gt;--cron&lt;/span&gt; &lt;span class="s2"&gt;"0 3 * * *"&lt;/span&gt;  &lt;span class="c"&gt;# daily at 3am UTC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Backups are encrypted, retained per your plan limits, and downloadable on demand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-download &amp;lt;postgres-service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--out&lt;/span&gt; ./dump.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or share with a teammate via a signed URL with a 30-second to 1-hour TTL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-download &amp;lt;postgres-service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--share&lt;/span&gt; &lt;span class="nt"&gt;--ttl&lt;/span&gt; 600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: scale when the prototype takes off
&lt;/h2&gt;

&lt;p&gt;The day the post hits the front page of Hacker News:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy scale my-v0-app 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Next.js app scales to 5 replicas behind the load balancer. Postgres and the bucket stay as the single source of truth for state. The frontend layer is stateless, so scaling horizontally is safe.&lt;/p&gt;

&lt;p&gt;When traffic dies down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy scale my-v0-app 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why one platform instead of three
&lt;/h2&gt;

&lt;p&gt;Every additional vendor in the path between your code and your data adds three costs you do not see on day one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Operational tax.&lt;/strong&gt; Each dashboard needs a login, a billing setup, an IAM model, and a backup story. Each is a place a breach or an expired credit card can stop your app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency.&lt;/strong&gt; Database in one region, object store in another, frontend on edge: each network hop is real milliseconds on every request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recovery surface.&lt;/strong&gt; When something breaks you debug across vendor boundaries. Slack threads with five companies' support teams resolve slower than one ticket with one provider.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Consolidating the backend into one platform does not preempt best-of-breed choices later. You can run NEXUS AI as the prototype platform and migrate the database to a dedicated provider after product-market fit. &lt;code&gt;nexus db backup-download&lt;/code&gt; gives you a portable Postgres dump whenever you want to leave.&lt;/p&gt;

&lt;h2&gt;
  
  
  Patterns that scale beyond v0
&lt;/h2&gt;

&lt;p&gt;The same pattern works for Lovable, Bolt, Replit, and any framework v0 supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lovable.&lt;/strong&gt; Push the Lovable export to GitHub, deploy with &lt;code&gt;--framework nextjs&lt;/code&gt; or &lt;code&gt;--framework vite&lt;/code&gt;, attach Postgres and a bucket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bolt.new.&lt;/strong&gt; Same flow. Bolt generates standalone Next.js or Vite projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replit.&lt;/strong&gt; Connect your Replit GitHub mirror, deploy with the appropriate framework flag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code or Cursor generated apps.&lt;/strong&gt; Direct push, direct deploy. Claude Code can also call the NEXUS AI MCP tools and deploy itself (see &lt;a href="https://nexusai.run/blog/mcp-driven-deploys-5-tasks-your-claude-agent-should-run" rel="noopener noreferrer"&gt;MCP-driven deploys&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The deploy command stays the same. The only thing that changes is the framework flag.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does v0 work with NEXUS AI directly?&lt;/strong&gt;&lt;br&gt;
v0 generates a Next.js project. NEXUS AI deploys Next.js projects from any Git repository. There is no special integration required. Push to GitHub, run &lt;code&gt;nexus deploy source --framework nextjs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need to write a Dockerfile?&lt;/strong&gt;&lt;br&gt;
No. NEXUS AI detects Next.js and generates a multi-stage production Dockerfile (build with &lt;code&gt;npm run build&lt;/code&gt;, run with &lt;code&gt;node server.js&lt;/code&gt; or &lt;code&gt;next start&lt;/code&gt; as appropriate).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I keep my frontend on Vercel and put only the backend on NEXUS AI?&lt;/strong&gt;&lt;br&gt;
Yes. Deploy just an API service (&lt;code&gt;nexus deploy source --framework express&lt;/code&gt; for a standalone Express backend, or a Next.js app with only &lt;code&gt;app/api/&lt;/code&gt; routes used). Point your Vercel frontend at the NEXUS AI URL via &lt;code&gt;NEXT_PUBLIC_API_URL&lt;/code&gt;. You get Vercel's CDN and NEXUS AI's stateful backend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does Postgres survive container restarts?&lt;/strong&gt;&lt;br&gt;
Yes. The Postgres data lives on a persistent volume attached to the deployment. Volumes survive container restarts, redeploys, and host reboots. See &lt;a href="https://nexusai.run/blog/how-nexus-ai-keeps-your-postgres-alive-across-host-reboots" rel="noopener noreferrer"&gt;How NEXUS AI keeps your Postgres alive across host reboots&lt;/a&gt; for the implementation details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What about authentication?&lt;/strong&gt;&lt;br&gt;
NEXUS AI does not ship a managed auth provider. Use Auth.js (NextAuth), Clerk, or Supabase Auth from your Next.js app. The auth provider stores session state in your NEXUS AI Postgres or Redis service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can my AI agent run all of this for me?&lt;/strong&gt;&lt;br&gt;
Yes. Connect the NEXUS AI MCP server to Claude Code, Cursor, or another MCP client. The agent calls &lt;code&gt;nexusai_deploy_source&lt;/code&gt;, &lt;code&gt;nexusai_bucket_create&lt;/code&gt;, &lt;code&gt;nexusai_bucket_attach&lt;/code&gt;, and &lt;code&gt;nexusai_db_query&lt;/code&gt; to do everything in this post end-to-end. See &lt;a href="https://nexusai.run/blog/mcp-driven-deploys-5-tasks-your-claude-agent-should-run" rel="noopener noreferrer"&gt;MCP-driven deploys&lt;/a&gt; for the 60-second setup.&lt;/p&gt;

&lt;p&gt;The deploy gap from v0 prototype to production app used to be the bottleneck. With one CLI command and one platform it collapses to about 5 minutes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nexusai.run/register" rel="noopener noreferrer"&gt;Start free.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>v0</category>
      <category>nextjs</category>
      <category>postgres</category>
      <category>lovable</category>
    </item>
    <item>
      <title>Database service backups in NEXUS AI</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Sun, 09 Aug 2026 13:02:03 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/database-service-backups-in-nexus-ai-2ff4</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/database-service-backups-in-nexus-ai-2ff4</guid>
      <description>&lt;h1&gt;
  
  
  Database service backups in NEXUS AI: backup, in-place restore, and cross-service restore
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Published:&lt;/strong&gt; May 9, 2026&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Databases · DevOps&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time:&lt;/strong&gt; 10 minutes&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Author:&lt;/strong&gt; NEXUS AI Team&lt;/p&gt;

&lt;p&gt;Database backups should be boring. You should be able to create one, download it, restore it into the same service, or restore it into a replacement deployment without rebuilding the whole application stack by hand.&lt;/p&gt;

&lt;p&gt;NEXUS AI database services now support that full lifecycle for deployment-attached database services. You can back up PostgreSQL, MySQL, MongoDB, and Redis services, restore a backup back into the original service, or restore it into another compatible database service in a different deployment under the same organization.&lt;/p&gt;

&lt;p&gt;This post walks through the complete workflow using the dashboard, REST API, CLI, and MCP tools.&lt;/p&gt;
&lt;h2&gt;
  
  
  What database backups cover
&lt;/h2&gt;

&lt;p&gt;NEXUS AI backups are created from database services attached to a deployment through Additional Services.&lt;/p&gt;

&lt;p&gt;Supported engines:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Backup format&lt;/th&gt;
&lt;th&gt;Restore behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pg_dump&lt;/code&gt; custom dump&lt;/td&gt;
&lt;td&gt;Restores database objects with clean/replace behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL&lt;/td&gt;
&lt;td&gt;SQL dump&lt;/td&gt;
&lt;td&gt;Imports the dump into the target database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MongoDB&lt;/td&gt;
&lt;td&gt;compressed &lt;code&gt;mongodump&lt;/code&gt; archive&lt;/td&gt;
&lt;td&gt;Restores collections with drop behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;RDB snapshot&lt;/td&gt;
&lt;td&gt;Replaces the target Redis dataset&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Backups are organization-scoped. A backup created in one organization cannot be restored into another organization. Cross-service restore is allowed only when the target service belongs to the same organization and uses a compatible database engine.&lt;/p&gt;

&lt;p&gt;Backups do not include application code, container images, secrets, deployment configuration, or filesystem volumes. They cover the database service data only.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create a database backup
&lt;/h2&gt;

&lt;p&gt;First, find the database service you want to back up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db services
nexus db services &amp;lt;deployment-name-or-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the backup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup &amp;lt;service-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List backups for that service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backups &amp;lt;service-id&amp;gt;
nexus db backups &amp;lt;service-id&amp;gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also create a backup through the REST API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NEXUS_API_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://nexusai.run/api"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NEXUS_JWT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_JWT_FROM_LOGIN"&lt;/span&gt;

curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_BASE&lt;/span&gt;&lt;span class="s2"&gt;/deployment-services/&amp;lt;serviceId&amp;gt;/backup"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_JWT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NEXUS AI runs the native database backup tool inside the running database container and stores the backup record for later download or restore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Download a backup
&lt;/h2&gt;

&lt;p&gt;For local retention, migration testing, or compliance workflows, download the backup file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-download &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt;
nexus db backup-download &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--out&lt;/span&gt; ./backups/prod-postgres.dump
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To create a short-lived signed download URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-download &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--share&lt;/span&gt;
nexus db backup-download &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--share&lt;/span&gt; &lt;span class="nt"&gt;--ttl&lt;/span&gt; 900
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The REST API supports both direct downloads and signed URLs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; backup.dump &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_BASE&lt;/span&gt;&lt;span class="s2"&gt;/deployment-services/&amp;lt;serviceId&amp;gt;/backups/&amp;lt;backupId&amp;gt;/download"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_JWT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_BASE&lt;/span&gt;&lt;span class="s2"&gt;/deployment-services/&amp;lt;serviceId&amp;gt;/backups/&amp;lt;backupId&amp;gt;/download-url"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_JWT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"ttlSeconds":900}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Treat downloaded backup files as sensitive. They may contain production user data, application data, credentials stored in tables, or regulated records.&lt;/p&gt;

&lt;h2&gt;
  
  
  Restore into the same service
&lt;/h2&gt;

&lt;p&gt;Use in-place restore when you want to roll a database service back to a known backup.&lt;/p&gt;

&lt;p&gt;CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db restore &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt;
nexus db restore &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;REST:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_BASE&lt;/span&gt;&lt;span class="s2"&gt;/deployment-services/&amp;lt;serviceId&amp;gt;/restore"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_JWT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"backupId":"&amp;lt;backupId&amp;gt;"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use in-place restore carefully. Restore can overwrite, clean, drop, or replace data depending on the database engine. Before restoring, create a fresh backup of the current state if you may need to roll forward again.&lt;/p&gt;

&lt;p&gt;Recommended in-place restore workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Put the app into maintenance mode or stop write-heavy workers.&lt;/li&gt;
&lt;li&gt;Create a fresh backup of the current service.&lt;/li&gt;
&lt;li&gt;Restore the selected backup.&lt;/li&gt;
&lt;li&gt;Restart or redeploy application containers if they cache connections or schema metadata.&lt;/li&gt;
&lt;li&gt;Validate critical flows before sending normal traffic back to the deployment.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Restore into another deployment service
&lt;/h2&gt;

&lt;p&gt;Cross-service restore is useful when you want to restore production data into a staging deployment, migrate data to a replacement deployment, test a backup without touching the source service, or recover into a newly created database service.&lt;/p&gt;

&lt;p&gt;The target service must be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the same organization as the backup.&lt;/li&gt;
&lt;li&gt;A compatible database engine.&lt;/li&gt;
&lt;li&gt;Running and reachable by NEXUS AI.&lt;/li&gt;
&lt;li&gt;Safe to overwrite.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db restore-to &amp;lt;target-service-id&amp;gt; &amp;lt;backup-id&amp;gt;
nexus db restore-to &amp;lt;target-service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;
nexus db restore-to &amp;lt;target-service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;REST:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_API_BASE&lt;/span&gt;&lt;span class="s2"&gt;/deployment-services/&amp;lt;targetServiceId&amp;gt;/restore-from/&amp;lt;backupId&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$NEXUS_JWT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MCP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nexusai_db_restore_to
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP tool requires &lt;code&gt;deployments:create&lt;/code&gt; scope. When using an AI client, include the backup ID and target service ID explicitly, then confirm the destructive operation before the tool call.&lt;/p&gt;

&lt;p&gt;Backend integrations can call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;backupService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restoreBackup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backupId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;targetServiceId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;targetServiceId&lt;/code&gt; option is optional. Existing in-place restore calls continue to work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;backupService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restoreBackup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backupId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example: restore production data into staging
&lt;/h2&gt;

&lt;p&gt;This workflow copies a production database backup into a staging database service under the same organization.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Find production database service&lt;/span&gt;
nexus db services prod-api

&lt;span class="c"&gt;# 2. Create a production backup&lt;/span&gt;
nexus db backup &amp;lt;prod-service-id&amp;gt;

&lt;span class="c"&gt;# 3. List backups and copy the backup id&lt;/span&gt;
nexus db backups &amp;lt;prod-service-id&amp;gt;

&lt;span class="c"&gt;# 4. Find the staging database service&lt;/span&gt;
nexus db services staging-api

&lt;span class="c"&gt;# 5. Restore the production backup into staging&lt;/span&gt;
nexus db restore-to &amp;lt;staging-service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After restore, restart or redeploy the staging app if it caches database connections, schema state, or application-level lookup data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy redeploy &amp;lt;staging-deployment-id&amp;gt; &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example: recover into a replacement deployment
&lt;/h2&gt;

&lt;p&gt;If a deployment is being replaced, create the new deployment with the same database service type, then restore the backup into the new database service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create or identify the replacement deployment&lt;/span&gt;
nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/your-org/your-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; api-replacement &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; docker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--services&lt;/span&gt; postgresql &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wait&lt;/span&gt;

&lt;span class="c"&gt;# Find the replacement PostgreSQL service&lt;/span&gt;
nexus db services api-replacement

&lt;span class="c"&gt;# Restore the backup into the replacement service&lt;/span&gt;
nexus db restore-to &amp;lt;replacement-postgres-service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets you validate the replacement deployment before cutting traffic over.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safety checklist before restore
&lt;/h2&gt;

&lt;p&gt;Before any restore, confirm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The backup ID is correct.&lt;/li&gt;
&lt;li&gt;The target service ID is correct.&lt;/li&gt;
&lt;li&gt;The target service belongs to the same organization.&lt;/li&gt;
&lt;li&gt;The target service uses the same database engine.&lt;/li&gt;
&lt;li&gt;You understand that target data may be overwritten.&lt;/li&gt;
&lt;li&gt;You have a fresh backup of the target service if rollback matters.&lt;/li&gt;
&lt;li&gt;Application workers are paused if they may write during restore.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For production services, schedule a maintenance window. Database restore is a data-changing operation, not a harmless read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can I restore a backup into a different deployment?
&lt;/h3&gt;

&lt;p&gt;Yes. Use &lt;code&gt;nexus db restore-to &amp;lt;target-service-id&amp;gt; &amp;lt;backup-id&amp;gt;&lt;/code&gt; or &lt;code&gt;POST /api/deployment-services/:targetServiceId/restore-from/:backupId&lt;/code&gt;. The target service must be in the same organization and compatible with the backup engine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I restore across organizations?
&lt;/h3&gt;

&lt;p&gt;No. Cross-organization restore is blocked. Backups and database services are organization-scoped to prevent cross-tenant data access.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does restore overwrite existing data?
&lt;/h3&gt;

&lt;p&gt;Yes. Restore can overwrite, clean, drop, or replace existing data depending on the engine. Treat every restore as destructive unless you have verified otherwise for your specific database and backup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does the old in-place restore endpoint still work?
&lt;/h3&gt;

&lt;p&gt;Yes. Existing restore flows still work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db restore &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;POST /api/deployment-services/:serviceId/restore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new cross-service restore path adds a target-service restore option without removing the old behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I use download-and-manual-restore instead?
&lt;/h3&gt;

&lt;p&gt;Use built-in restore for same-org NEXUS AI database services. Use manual restore when moving data outside NEXUS AI, restoring into an external managed database, or performing a custom migration that needs database-native flags not exposed by the platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;NEXUS AI database backups are designed for the actual recovery workflows teams need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Back up a running database service.&lt;/li&gt;
&lt;li&gt;Download the backup for retention or audit.&lt;/li&gt;
&lt;li&gt;Restore in place when you need rollback.&lt;/li&gt;
&lt;li&gt;Restore into another same-org service when you need staging refreshes, migration testing, or deployment replacement.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important rule is simple: choose the target service carefully. Restore is powerful because it changes real data.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>postgres</category>
      <category>backup</category>
    </item>
  </channel>
</rss>
