megacolorboy

Abdush Shakoor's Weblog

Writings, experiments & ideas.

Create, extract, and inspect tar archives

Whenever I need to bundle a directory before moving it between servers, tar is a useful tool to have around. I thought I'd write down the few commands I keep coming back to.

Let's say you have an uploads directory in your current location. To create a gzip-compressed archive:

tar -czf uploads.tar.gz uploads/

Here, -c creates the archive, -z uses gzip compression, and -f specifies the archive filename.

If the directory is somewhere else, you can use -C to choose the source directory without storing its entire path:

tar -czf uploads.tar.gz -C /var/www/html/public uploads/

These are alternative ways to create the same archive. Replace the paths with your own and choose the one that suits your situation.

Before extracting it, you can inspect the contents:

tar -tzvf uploads.tar.gz

For example, a listing might look like this:

drwxr-xr-x devops/devops       0 2026-09-20 09:00 uploads/
-rw-r--r-- devops/devops    2048 2026-09-20 09:00 uploads/logo.png
-rw-r--r-- devops/devops    4096 2026-09-20 09:00 uploads/banner.jpg

The -t flag lists entries, and -v adds details such as permissions and sizes.

To extract into your current directory:

tar -xzvf uploads.tar.gz

Or extract into a specific directory, creating it first if needed:

mkdir -p ./restored
tar -xzvf uploads.tar.gz -C ./restored

This puts the files under ./restored/uploads/. The -x flag extracts the archive, and verbose output might look like this:

uploads/
uploads/logo.png
uploads/banner.jpg

I find it helpful to list the contents first so I know which directory structure to expect. Extracting into an empty directory also helps avoid overwriting existing files.

Hope you found this useful!

Test API CORS headers using curl

When troubleshooting an API request from a browser, I find it useful to inspect the CORS headers directly with curl. It makes it easier to see what the server is returning.

For a cross-origin JSON POST request with an authorization header, start by simulating the browser's preflight request:

curl -i -X OPTIONS 'https://api.example.com/endpoint' \
  -H 'Origin: https://example.com' \
  -H 'Access-Control-Request-Method: POST' \
  -H 'Access-Control-Request-Headers: content-type, authorization'

Replace both URLs with your API endpoint and frontend origin. An illustrative successful response could look like this:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: content-type, authorization
Vary: Origin

Next, test the actual POST request:

curl -i 'https://api.example.com/endpoint' \
  -H 'Origin: https://example.com' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --data '{"message":"Testing CORS"}'

Use a suitable test endpoint and payload: this sends a real POST request. Replace the token if authentication is required, or remove that header if it isn't.

Notice that the Access-Control-Request-* headers belong to the preflight. The actual POST carries the content type and authorization header themselves.

An example POST response might be:

HTTP/1.1 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: https://example.com
Vary: Origin

{"message":"Request received"}

Check the CORS headers on both responses. A successful preflight alone isn't enough if the actual response is missing the required headers.

For browser requests made with credentials, such as cookies using credentials: 'include', the server must allow the specific origin and return Access-Control-Allow-Credentials: true; a wildcard origin won't work for those requests.

Finally, curl displays the response but doesn't enforce CORS like a browser does. Confirm the behavior in your browser too. The MDN CORS guide explains how the browser evaluates these responses.

Hope you found this tip useful!

View a Kubernetes ConfigMap using kubectl

When checking an application's configuration, I often want to see what is actually stored in its ConfigMap. You can get that straight from the terminal.

First, list the ConfigMaps in your namespace:

kubectl get configmaps -n staging

Then view the one you're interested in as YAML:

kubectl get configmap web-config -n staging -o yaml

Replace web-config and staging with your own values. Here's an illustrative response with some metadata omitted:

apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
  namespace: staging
data:
  APP_ENV: staging
  LOG_LEVEL: info
  API_BASE_URL: https://api.example.com

If you only need a single value, JSONPath keeps the output small:

kubectl get configmap web-config -n staging -o jsonpath='{.data.LOG_LEVEL}{"\n"}'

For the example above, that prints:

info

One thing to remember: this shows the ConfigMap stored in Kubernetes. It doesn't prove that the application is already using its latest values.

Values injected as environment variables require a pod restart to pick up changes. Mounted ConfigMap files generally update eventually, but subPath mounts don't receive those updates, and the application may still need to reload the file. The ConfigMap documentation explains these differences.

ConfigMaps are intended for non-confidential configuration. Passwords and tokens belong in Secrets or your existing secrets-management system.

Hope you found this useful!

Check connectivity from a Node.js application pod

I've deployed quite a few Node.js applications over the past three months. Sometimes I just want to do a basic connectivity check from the application's container without installing another tool.

Since Node.js is already there, why not use it?

For a hostname lookup using the container's operating-system resolver, try this:

kubectl exec web-7c9d6f8b5d-k2m4p -n staging -c app -- node -e "require('node:dns').lookup('example.com', { all: true }, (error, addresses) => { if (error) { console.error(error); process.exitCode = 1; return; } console.log(addresses); })"

Replace the pod name, namespace, container name, and hostname with your own. An illustrative result might look like this:

[ { address: '192.0.2.10', family: 4 } ]

That address is only an example, not the actual address of example.com. Also, dns.lookup() uses the system's name-resolution facilities, which may include /etc/hosts; it isn't necessarily a direct DNS query. The Node.js DNS documentation explains the distinction.

To go a step further and make an HTTPS request, use a Node.js runtime with built-in fetch and AbortSignal.timeout support, such as Node.js 18 or later:

kubectl exec web-7c9d6f8b5d-k2m4p -n staging -c app -- node -e "fetch('https://example.com', { signal: AbortSignal.timeout(10000) }).then(response => { console.log('HTTP status:', response.status); if (!response.ok) process.exitCode = 1; }).catch(error => { console.error(error); process.exitCode = 1; })"

An example response is:

HTTP status: 200

This checks more than name resolution: the request must also connect and complete TLS negotiation before receiving an HTTP response. A 401 or 403 still shows that an HTTP server responded, even though access wasn't granted. See the Node.js fetch documentation.

These commands start a new Node.js process, so they won't automatically reproduce proxy settings or custom HTTP clients configured inside your running application.

Hope you found this tip useful!

Test port connectivity using curl's Telnet support

Honestly, I discovered this for the first time recently. I had always used telnet to check whether I could connect to a port, but I never knew curl could do this too.

Here's a quick example:

curl -v --connect-timeout 5 --max-time 10 telnet://mail.example.com:25

Replace the hostname and port with the service you're testing. The connection timeout gives it five seconds to establish a connection, while --max-time limits the whole session to ten seconds.

An illustrative excerpt from a reachable SMTP server might look like this:

*   Trying 192.0.2.25:25...
* Connected to mail.example.com (192.0.2.25) port 25
220 mail.example.com ESMTP Postfix

The Connected line confirms that the TCP connection opened. In this example, the SMTP banner also tells us that the server responded. Other services might stay silent until you send something.

Don't be surprised if the command eventually reports a timeout: the TCP connection may have succeeded and then remained open until the ten-second limit. Read the connection messages instead of relying only on the final exit code.

This doesn't verify authentication, TLS, or the health of an entire application. It's just a handy first check when troubleshooting connectivity.

If your curl build reports that Telnet isn't supported, run curl --version and check its protocol list. The curl tutorial includes more about its Telnet support.

Hope you found this useful!

Find out why a Kubernetes pod keeps crashing

If you've been deploying applications to Kubernetes for a while, you've probably come across CrashLoopBackOff. Seeing it is annoying, but the useful part is finding out what happened just before the container stopped.

Here's how I would start:

kubectl get pod web-7c9d6f8b5d-k2m4p -n staging

Replace the pod name and namespace with your own. For example, the output might be:

NAME                  READY   STATUS             RESTARTS      AGE
web-7c9d6f8b5d-k2m4p   0/1     CrashLoopBackOff   5 (34s ago)   8m

That shows the symptom. To investigate it, get the pod's details:

kubectl describe pod web-7c9d6f8b5d-k2m4p -n staging

Look at the container's state, last termination reason, exit code, and the events near the bottom. An illustrative excerpt could look like this:

State:          Waiting
  Reason:       CrashLoopBackOff
Last State:     Terminated
  Reason:       Error
  Exit Code:    1

Next, check the application logs:

kubectl logs web-7c9d6f8b5d-k2m4p -n staging -c app --tail=100

If the container has already restarted, the previous instance's logs can be more useful:

kubectl logs web-7c9d6f8b5d-k2m4p -n staging -c app --previous --tail=100

Here, app is the container name. The --previous flag retrieves logs from its previous terminated instance when available; it isn't a history of every restart. See the kubectl logs reference.

For example, your application might have logged:

Error: Missing required environment variable DATABASE_URL

Now you have something specific to investigate: the application's configuration.

If you see ImagePullBackOff or ErrImagePull instead, the image couldn't be pulled, so application logs may not exist yet. Start with the events from describe and check the image name, tag, registry access, and image-pull credentials. The Kubernetes debugging guide covers these checks.

Hope you found this tip useful!

Send a test email from Kubernetes using curl

When troubleshooting email delivery, I sometimes want to send a test message from inside the cluster. This helps narrow down whether the SMTP server is reachable from that environment.

For a quick test, you can start a temporary pod with curl installed:

kubectl run smtp-test -n staging --rm -it --restart=Never --image=curlimages/curl --command -- sh

Replace staging with your namespace. This creates a separate pod. --command selects the shell, and --restart=Never prevents the container from restarting when it exits. During a normal attached session, --rm cleans up the pod when you're done. See the kubectl run reference.

Inside the shell, send a message through your internal SMTP relay:

curl -v --connect-timeout 5 --max-time 30 \
  --url smtp://mail.example.com:25 \
  --mail-from 'sender@example.com' \
  --mail-rcpt 'recipient@example.com' \
  --crlf --upload-file - <<'EOF'
From: sender@example.com
To: recipient@example.com
Subject: Test email from Kubernetes

This is a test message.
EOF

Replace the hostname and addresses with real ones before running it. This example assumes an internal relay that allows unauthenticated, unencrypted SMTP from the test pod.

For an authenticated server using STARTTLS on port 587, use these options in the same command:

--url smtp://mail.example.com:587 --ssl-reqd --user 'smtp-user'

With only a username supplied, curl prompts for the password. For implicit TLS, use smtps://mail.example.com:465 instead. These options are described in the curl manual.

An illustrative excerpt after uploading the message might look like this:

< 250 2.0.0 Ok: queued as A1B2C3D4
> QUIT
< 221 2.0.0 Bye

That means the relay accepted the message, not that it has reached the recipient's inbox. Check delivery logs if it never arrives.

Also, a separate test pod may have different labels and network policies from your application. Treat this as a test from that pod, rather than proof that the application has identical access.

Type exit to finish. If the session was interrupted and the pod remains, remove it with kubectl delete pod smtp-test -n staging.

Hope you found this useful!

Restart Kubernetes deployments using kubectl

I usually restart application pods using Argo CD or Rancher. That's convenient for one or two applications, but when there are several deployments to restart, I'd rather do it from the terminal.

First, check the current cluster context and the deployments in your namespace:

kubectl config current-context
kubectl get deployments -n staging

Then trigger a restart for one deployment:

kubectl rollout restart deployment/web -n staging

An example response is:

deployment.apps/web restarted

Replace web and staging with your deployment and namespace. The response confirms that the restart was requested, so I also watch its progress:

kubectl rollout status deployment/web -n staging --timeout=120s

Once complete, you might see:

deployment "web" successfully rolled out

To restart all deployments in that namespace:

kubectl rollout restart deployment -n staging

Or narrow it down using a label that exists on your deployments:

kubectl rollout restart deployment -n staging -l app=web

One distinction worth remembering: this replaces the pods managed by the selected deployments. It doesn't restart every kind of pod in the namespace. Replacement behavior follows each deployment's strategy, so availability still depends on its configuration and available capacity.

You can find more examples in the kubectl rollout reference.

Hope you found this useful!