Skip to content

Dev pod

A dev pod is an ephemeral interactive environment — spin it up with kubectl apply, connect via port-forward, do your work, and tear it down when you are done. No GitOps ceremony, no Ingress, no Service. This is the explicit exception to the “commit everything to your repo” rule: dev pods are throwaway by design, and managing them through ArgoCD is ceremonial overhead.

Before using this recipe, confirm:

  • Your tenant is provisioned and you have completed Your first deployment end-to-end.
  • You have kubectl access to your tenant namespaces.
  • You know your tenant name (the <your-tenant> prefix used in namespace names).

Use this recipe when you need:

  • A short-lived interactive environment (JupyterLab, RStudio, a debug shell).
  • Quick prototyping or data exploration without committing manifests to your repo.
  • A sandbox that you will delete within hours or days — not a persistent service.

If your workload runs continuously and serves traffic, see Long-running service. If your workload runs to completion and exits, see Jobs & CronJobs.

Save this manifest locally (not in your workload repo). Replace <your-tenant> with your real tenant name.

dev-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: jupyter
namespace: <your-tenant>-dev
labels:
app: jupyter
spec:
priorityClassName: uber-user-significant
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: notebook
image: quay.io/jupyter/minimal-notebook:2024-08-19
ports:
- containerPort: 8888
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 2
memory: 4Gi
volumeMounts:
- name: home
mountPath: /home/jovyan
- name: tmp
mountPath: /tmp
volumes:
- name: home
emptyDir: {}
- name: tmp
emptyDir: {}

Before applying, validate the manifest locally:

Terminal window
kubectl apply --dry-run=client -f dev-pod.yaml

This should render without errors. It catches YAML syntax issues and missing required fields before the cluster tries to create the pod.

Apply the pod directly — no commit, no ArgoCD:

Terminal window
kubectl apply -f dev-pod.yaml

Wait for the pod to start:

Terminal window
kubectl get pod jupyter -n <your-tenant>-dev --watch

Once the status shows Running, read the Jupyter access token from the logs:

Terminal window
kubectl logs jupyter -n <your-tenant>-dev

Look for a line like:

http://127.0.0.1:8888/lab?token=abc123...

Copy the token value. Then start port-forwarding:

Terminal window
kubectl port-forward -n <your-tenant>-dev pod/jupyter 8888:8888

Open your browser to http://localhost:8888/?token=<token> (paste the token from the logs). The JupyterLab interface should load.

When you are done, delete the pod:

Terminal window
kubectl delete pod jupyter -n <your-tenant>-dev

Everything in the emptyDir volumes is deleted with the pod. If you created notebooks or data you want to keep, download them from JupyterLab before deleting.

  • Pod rejected by Kyverno with PSS admission error. You modified the securityContext block. Revert to the verbatim YAML above. See Known limitations for the full PSS rule.
  • Namespace rejected with forceTenantPrefix. The namespace name does not start with your tenant name. All namespaces must be <your-tenant>-<suffix>. See Known limitations.
  • Pod stuck in Pending. Check kubectl describe pod -n <your-tenant>-dev jupyter — the Events section shows the reason. Common cause: quota exceeded. Request more via RCS ticket, or delete other workloads in your tenant to free capacity.
  • Jupyter shows Permission denied errors. The emptyDir mount at /home/jovyan may be missing. Verify the volumeMounts and volumes sections match the YAML above exactly.
  • Port-forward disconnects or hangs. This is a known kubectl behavior — it drops idle connections. Re-run the kubectl port-forward command. Your Jupyter session state persists as long as the pod is still running.
  • Want this notebook to persist long-term? Promote it to a Long-running service — commit a Deployment manifest to your workload repo with the same image and resource requests, add a Service and Ingress for stable access, and let ArgoCD manage the lifecycle.