🚀 Executive Summary

TL;DR: Deploying applications reliably involves navigating environment differences, dependencies, and human error. The article outlines three methods: direct server access (fragile), CI/CD pipelines (modern standard, recommended start), and containerization with orchestration (scalable, cloud-native), emphasizing CI/CD for consistent, automated deployments.

🎯 Key Takeaways

  • The “Cowboy” method (direct SSH/manual commands) is fast for personal projects or emergencies but is inherently fragile, lacks automated testing, and leads to inconsistent environments, making it unsuitable for production.
  • CI/CD pipelines automate the entire deployment process, from build and test to secure deployment, providing high reliability, a full audit trail, and acting as a critical gatekeeper with automated testing.
  • Containerization with orchestration (e.g., Docker, Kubernetes) deploys self-contained, versioned application images, ensuring environment consistency from dev to prod and enabling very high scalability and resilience through rolling updates.

What are the go-to ways to deploy apps?

From manual SSH commands to fully automated Kubernetes pipelines, here’s a senior engineer’s breakdown of the three main ways to deploy your applications in the real world.

So, You Wrote Some Code. Now What? A Senior Engineer’s Guide to Actually Deploying Your App.

I still remember the feeling. It was 4:55 PM on a Friday, my first year on the job. A “critical” one-line hotfix needed to go out. The lead engineer was out, and the deployment process was a sacred document titled `deploy_prod.txt`. The steps were simple: SSH into `prod-web-01`, navigate to `/var/www/html`, run `git pull origin master`, and finally, `sudo service apache2 restart`. I followed the steps. The site went down. Hard. Turns out, a dependency had been updated in `master` that wasn’t installed on the server. That panic, the cold sweat, the frantic scramble through logs—that’s why the question “What are the go-to ways to deploy apps?” isn’t just a simple technical query. It’s a question about reliability, sanity, and not ruining your weekend.

The “Why”: What’s the Big Deal?

At its core, “deployment” is just the act of getting your code from your laptop onto a server where users can access it. The problem is the chasm between those two states. It’s filled with potential landmines: environment differences, missing dependencies, database migration failures, and human error. The goal of a good deployment strategy is to make this process repeatable, reliable, and, ideally, boring. The reason there are so many “go-to” ways is that the “right” way depends entirely on your team’s size, your application’s complexity, and how much you enjoy sleeping soundly at night.

Let’s break down the common approaches I see in the wild, from the quick-and-dirty to the enterprise-grade.

Solution 1: The “Cowboy” Method (Direct Server Access)

This is the modern equivalent of using an FTP client to upload files. You connect directly to your server via SSH and manually pull or copy your code. It’s the method from my horror story, and while it gets a lot of flak, almost every engineer has done it at some point.

How it works:

  1. You SSH into your production server: ssh user@prod-web-01
  2. You navigate to your application directory: cd /srv/my-app
  3. You pull the latest code: git pull origin main
  4. You install dependencies and restart the service: npm install && pm2 restart my-app

You can even get slightly more sophisticated with a simple bash script to automate these steps:


#!/bin/bash
# deploy.sh - A script that will probably get you fired.

set -e # Exit immediately if a command exits with a non-zero status.

echo "Deploying application..."

# Navigate to app directory
cd /srv/my-app || exit

# Pull latest code
git pull origin main

# Install dependencies
npm install --production

# Restart the application
pm2 restart my-app

echo "Deployment finished!"

Warning: This is fast but incredibly fragile. There are no tests, no build steps, no safety net. A single bad commit can take down your entire application instantly. It doesn’t scale beyond one or two servers and is a recipe for inconsistent environments. Use this only for personal projects or emergencies.

Solution 2: The Modern Standard (CI/CD Pipelines)

This is where most professional teams live. A CI/CD (Continuous Integration/Continuous Deployment) pipeline automates the entire process. The trigger is usually a `git push` to a specific branch (like `main` or `release`). A dedicated service like GitLab CI, GitHub Actions, or Jenkins then takes over.

How it works:

The process is defined in a configuration file within your repository (e.g., `.gitlab-ci.yml`). When you push code, the CI/CD runner executes a series of “stages”:

  • Build: Compiles your code, bundles assets.
  • Test: Runs unit tests, integration tests, and linters. If any test fails, the pipeline stops.
  • Deploy: If tests pass, the pipeline securely connects to your server(s) and deploys the new code. This is often done by packaging the build into an artifact and using a tool like `rsync` or SSH to transfer it.

Here’s a simplified example for GitLab CI:


# .gitlab-ci.yml
stages:
  - test
  - deploy

run_tests:
  stage: test
  image: node:18
  script:
    - npm install
    - npm test

deploy_production:
  stage: deploy
  script:
    - echo "Deploying to production server..."
    - ssh user@prod-web-01 "cd /srv/my-app && git pull && npm install --production && pm2 restart my-app"
  only:
    - main

Pro Tip: This is the sweet spot for most applications. It introduces automated testing as a gatekeeper, ensures a consistent process, and removes the need for engineers to have direct production server access. You get a full audit trail of who deployed what and when.

Solution 3: The “All-In” Approach (Containerization & Orchestration)

This is the cloud-native way. Instead of deploying your *code*, you deploy a self-contained, versioned *image* of your application. Tools like Docker package your app, its code, its runtime, and all its dependencies into a single, portable unit. Then, an orchestrator like Kubernetes (K8s) or AWS ECS manages running these container images at scale.

How it works:

The deployment pipeline gets an extra step. Instead of just building and testing, it builds a Docker image and pushes it to a container registry (like Docker Hub or AWS ECR).

  1. You define your application environment in a Dockerfile.
  2. Your CI/CD pipeline runs `docker build` to create the image and `docker push` to upload it to a registry.
  3. The pipeline then tells your orchestrator (Kubernetes) to perform a rolling update, replacing the old running containers with the new image version, with zero downtime.

A simple Dockerfile for a Node.js app might look like this:


# Dockerfile
# Use an official Node runtime as a parent image
FROM node:18-alpine

# Set the working directory
WORKDIR /usr/src/app

# Copy package.json and install dependencies
COPY package*.json ./
RUN npm install --production

# Bundle app source
COPY . .

# Expose the port the app runs on
EXPOSE 3000

# Define the command to run your app
CMD [ "node", "server.js" ]

This approach completely solves the “it works on my machine” problem. The environment is identical from development to production because it’s baked into the image. It also makes scaling and resilience trivial for the orchestrator to handle.

So, Which One Should I Use?

Let’s be real, it’s about trade-offs. Here’s how I see it:

Method Speed (for setup) Reliability Scalability Complexity
1. Cowboy (SSH/Manual) Lightning Fast Very Low Very Low Trivial
2. CI/CD Pipeline Moderate High Moderate Moderate
3. Containers & Orchestration Slow Very High Very High High

My advice? Skip the “Cowboy” method for anything you care about. Start with a CI/CD pipeline (Solution 2). It’s the foundational skill for modern software development. Once your application grows, starts needing to scale, or is broken into microservices, you’ll naturally feel the pull towards containers and orchestration (Solution 3). Don’t jump straight to Kubernetes for your personal blog, but don’t deploy your company’s flagship product with a bash script either. Choose the tool that makes your deployments boring, reliable, and keeps your Friday nights free.

Darian Vance - Lead Cloud Architect

Darian Vance

Lead Cloud Architect & DevOps Strategist

With over 12 years in system architecture and automation, Darian specializes in simplifying complex cloud infrastructures. An advocate for open-source solutions, he founded TechResolve to provide engineers with actionable, battle-tested troubleshooting guides and robust software alternatives.


🤖 Frequently Asked Questions

âť“ What are the main approaches for deploying applications?

Applications are commonly deployed using three main approaches: direct server access (manual SSH), automated CI/CD pipelines (e.g., GitLab CI, GitHub Actions), and containerization with orchestration (e.g., Docker, Kubernetes).

âť“ What are the trade-offs between CI/CD pipelines and direct server access for deployment?

CI/CD pipelines offer high reliability, moderate scalability, and moderate complexity by automating testing and deployment. Direct server access is fast to set up but has very low reliability and scalability, being prone to human error and environment inconsistencies.

âť“ How do containerization and orchestration solve the “it works on my machine” problem?

Containerization (e.g., Docker) packages the application, its runtime, and all dependencies into a single, portable image. This ensures the environment is identical from development to production, completely solving the “it works on my machine” problem by eliminating environment differences.

Leave a Reply

Discover more from TechResolve - SaaS Troubleshooting & Software Alternatives

Subscribe now to keep reading and get access to the full archive.

Continue reading